diff --git a/.github/workflows/dependency_overrides.patch b/.github/workflows/dependency_overrides.patch index 4326cf28d59..81c48c38acf 100644 --- a/.github/workflows/dependency_overrides.patch +++ b/.github/workflows/dependency_overrides.patch @@ -17,9 +17,9 @@ diff --git a/packages/cookie_store/pubspec_overrides.yaml b/packages/cookie_stor dependency_overrides: cookie_store_conformance_tests: path: packages/cookie_store_conformance_tests -+ frontend_server_client: ^4.0.0 neon_lints: path: ../neon_lints ++ frontend_server_client: ^4.0.0 diff --git a/packages/dynamite/packages/dynamite_end_to_end_test/pubspec.yaml b/packages/dynamite/packages/dynamite_end_to_end_test/pubspec.yaml --- a/packages/dynamite/packages/dynamite_end_to_end_test/pubspec.yaml +++ b/packages/dynamite/packages/dynamite_end_to_end_test/pubspec.yaml @@ -34,17 +34,17 @@ diff --git a/packages/dynamite/packages/dynamite_end_to_end_test/pubspec.yaml b/ diff --git a/packages/dynamite/packages/dynamite_end_to_end_test/pubspec_overrides.yaml b/packages/dynamite/packages/dynamite_end_to_end_test/pubspec_overrides.yaml --- a/packages/dynamite/packages/dynamite_end_to_end_test/pubspec_overrides.yaml +++ b/packages/dynamite/packages/dynamite_end_to_end_test/pubspec_overrides.yaml -@@ -1,8 +1,10 @@ +@@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: dynamite,dynamite_runtime,neon_lints +# melos_managed_dependency_overrides: dynamite,dynamite_runtime,neon_lints,frontend_server_client,quiver dependency_overrides: dynamite: path: ../.. - dynamite_runtime: +@@ -6,3 +6,5 @@ dependency_overrides: path: ../dynamite_runtime -+ frontend_server_client: ^4.0.0 neon_lints: path: ../../../neon_lints ++ frontend_server_client: ^4.0.0 + quiver: ^3.2.1 diff --git a/packages/dynamite/packages/dynamite_runtime/pubspec.yaml b/packages/dynamite/packages/dynamite_runtime/pubspec.yaml --- a/packages/dynamite/packages/dynamite_runtime/pubspec.yaml @@ -52,7 +52,7 @@ diff --git a/packages/dynamite/packages/dynamite_runtime/pubspec.yaml b/packages @@ -29,6 +29,9 @@ dev_dependencies: path: packages/neon_lints test: ^1.25.8 - + +dependency_overrides: + frontend_server_client: ^4.0.0 + @@ -66,9 +66,9 @@ diff --git a/packages/dynamite/packages/dynamite_runtime/pubspec_overrides.yaml -# melos_managed_dependency_overrides: neon_lints +# melos_managed_dependency_overrides: neon_lints,frontend_server_client dependency_overrides: -+ frontend_server_client: ^4.0.0 neon_lints: path: ../../../neon_lints ++ frontend_server_client: ^4.0.0 diff --git a/packages/dynamite/pubspec.yaml b/packages/dynamite/pubspec.yaml --- a/packages/dynamite/pubspec.yaml +++ b/packages/dynamite/pubspec.yaml @@ -87,9 +87,9 @@ diff --git a/packages/dynamite/pubspec_overrides.yaml b/packages/dynamite/pubspe -# melos_managed_dependency_overrides: neon_lints +# melos_managed_dependency_overrides: neon_lints,frontend_server_client,quiver dependency_overrides: -+ frontend_server_client: ^4.0.0 neon_lints: path: ../neon_lints ++ frontend_server_client: ^4.0.0 + quiver: ^3.2.1 diff --git a/packages/interceptor_http_client/pubspec.yaml b/packages/interceptor_http_client/pubspec.yaml --- a/packages/interceptor_http_client/pubspec.yaml @@ -111,9 +111,9 @@ diff --git a/packages/interceptor_http_client/pubspec_overrides.yaml b/packages/ dependency_overrides: cookie_store: path: ../cookie_store -+ frontend_server_client: ^4.0.0 neon_lints: path: ../neon_lints ++ frontend_server_client: ^4.0.0 + web: ">=0.5.0 <2.0.0" diff --git a/packages/neon_framework/packages/account_repository/pubspec.yaml b/packages/neon_framework/packages/account_repository/pubspec.yaml --- a/packages/neon_framework/packages/account_repository/pubspec.yaml @@ -132,27 +132,20 @@ diff --git a/packages/neon_framework/packages/account_repository/pubspec.yaml b/ diff --git a/packages/neon_framework/packages/account_repository/pubspec_overrides.yaml b/packages/neon_framework/packages/account_repository/pubspec_overrides.yaml --- a/packages/neon_framework/packages/account_repository/pubspec_overrides.yaml +++ b/packages/neon_framework/packages/account_repository/pubspec_overrides.yaml -@@ -1,9 +1,11 @@ +@@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box +# melos_managed_dependency_overrides: cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box,archive,image_picker,platform,quiver,win32 dependency_overrides: -+ archive: ^3.5.0 cookie_store: path: ../../../cookie_store - dynamite_runtime: - path: ../../../dynamite/packages/dynamite_runtime -+ image_picker: ^0.8.2 - interceptor_http_client: - path: ../../../interceptor_http_client - neon_framework: -@@ -16,5 +18,8 @@ dependency_overrides: - path: ../../../nextcloud - notifications_push_repository: +@@ -18,3 +18,8 @@ dependency_overrides: path: ../notifications_push_repository -+ platform: ^3.1.0 -+ quiver: ^3.2.1 sort_box: path: ../sort_box ++ archive: ^3.5.0 ++ image_picker: ^0.8.2 ++ platform: ^3.1.0 ++ quiver: ^3.2.1 + win32: ^5.5.1 diff --git a/packages/neon_framework/packages/dashboard_app/pubspec.yaml b/packages/neon_framework/packages/dashboard_app/pubspec.yaml --- a/packages/neon_framework/packages/dashboard_app/pubspec.yaml @@ -160,7 +153,7 @@ diff --git a/packages/neon_framework/packages/dashboard_app/pubspec.yaml b/packa @@ -39,6 +39,13 @@ dev_dependencies: url_launcher_platform_interface: ^2.3.2 vector_graphics_compiler: ^1.1.16 - + +dependency_overrides: + archive: ^3.5.0 + image_picker: ^0.8.2 @@ -174,29 +167,20 @@ diff --git a/packages/neon_framework/packages/dashboard_app/pubspec.yaml b/packa diff --git a/packages/neon_framework/packages/dashboard_app/pubspec_overrides.yaml b/packages/neon_framework/packages/dashboard_app/pubspec_overrides.yaml --- a/packages/neon_framework/packages/dashboard_app/pubspec_overrides.yaml +++ b/packages/neon_framework/packages/dashboard_app/pubspec_overrides.yaml -@@ -1,11 +1,13 @@ +@@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box +# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box,archive,image_picker,platform,quiver,win32 dependency_overrides: account_repository: path: ../account_repository +@@ -20,3 +20,8 @@ dependency_overrides: + path: ../notifications_push_repository + sort_box: + path: ../sort_box + archive: ^3.5.0 - cookie_store: - path: ../../../cookie_store - dynamite_runtime: - path: ../../../dynamite/packages/dynamite_runtime + image_picker: ^0.8.2 - interceptor_http_client: - path: ../../../interceptor_http_client - neon_framework: -@@ -18,5 +20,8 @@ dependency_overrides: - path: ../../../nextcloud - notifications_push_repository: - path: ../notifications_push_repository + platform: ^3.1.0 + quiver: ^3.2.1 - sort_box: - path: ../sort_box + win32: ^5.5.1 diff --git a/packages/neon_framework/packages/neon_http_client/pubspec.yaml b/packages/neon_framework/packages/neon_http_client/pubspec.yaml --- a/packages/neon_framework/packages/neon_http_client/pubspec.yaml @@ -213,23 +197,53 @@ diff --git a/packages/neon_framework/packages/neon_http_client/pubspec.yaml b/pa diff --git a/packages/neon_framework/packages/neon_http_client/pubspec_overrides.yaml b/packages/neon_framework/packages/neon_http_client/pubspec_overrides.yaml --- a/packages/neon_framework/packages/neon_http_client/pubspec_overrides.yaml +++ b/packages/neon_framework/packages/neon_http_client/pubspec_overrides.yaml -@@ -1,12 +1,15 @@ +@@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: cookie_store,dynamite_runtime,interceptor_http_client,neon_lints,nextcloud +# melos_managed_dependency_overrides: cookie_store,dynamite_runtime,interceptor_http_client,neon_lints,nextcloud,frontend_server_client,quiver,web dependency_overrides: cookie_store: path: ../../../cookie_store - dynamite_runtime: - path: ../../../dynamite/packages/dynamite_runtime -+ frontend_server_client: ^4.0.0 - interceptor_http_client: - path: ../../../interceptor_http_client - neon_lints: +@@ -10,3 +10,6 @@ dependency_overrides: path: ../../../neon_lints nextcloud: path: ../../../nextcloud ++ frontend_server_client: ^4.0.0 + quiver: ^3.2.1 + web: ">=0.5.0 <2.0.0" +diff --git a/packages/neon_framework/packages/neon_rich_text/pubspec.yaml b/packages/neon_framework/packages/neon_rich_text/pubspec.yaml +--- a/packages/neon_framework/packages/neon_rich_text/pubspec.yaml ++++ b/packages/neon_framework/packages/neon_rich_text/pubspec.yaml +@@ -34,5 +34,12 @@ dev_dependencies: + rxdart: ^0.28.0 + url_launcher_platform_interface: ^2.3.2 + ++dependency_overrides: ++ archive: ^3.5.0 ++ image_picker: ^0.8.2 ++ platform: ^3.1.0 ++ quiver: ^3.2.1 ++ win32: ^5.5.1 ++ + flutter: + uses-material-design: true +diff --git a/packages/neon_framework/packages/neon_rich_text/pubspec_overrides.yaml b/packages/neon_framework/packages/neon_rich_text/pubspec_overrides.yaml +--- a/packages/neon_framework/packages/neon_rich_text/pubspec_overrides.yaml ++++ b/packages/neon_framework/packages/neon_rich_text/pubspec_overrides.yaml +@@ -1,4 +1,4 @@ +-# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box ++# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box,archive,image_picker,platform,quiver,win32 + dependency_overrides: + account_repository: + path: ../account_repository +@@ -20,3 +20,8 @@ dependency_overrides: + path: ../notifications_push_repository + sort_box: + path: ../sort_box ++ archive: ^3.5.0 ++ image_picker: ^0.8.2 ++ platform: ^3.1.0 ++ quiver: ^3.2.1 ++ win32: ^5.5.1 diff --git a/packages/neon_framework/packages/neon_storage/pubspec.yaml b/packages/neon_framework/packages/neon_storage/pubspec.yaml --- a/packages/neon_framework/packages/neon_storage/pubspec.yaml +++ b/packages/neon_framework/packages/neon_storage/pubspec.yaml @@ -249,21 +263,18 @@ diff --git a/packages/neon_framework/packages/neon_storage/pubspec_overrides.yam dependency_overrides: cookie_store: path: ../../../cookie_store -@@ -6,6 +6,7 @@ dependency_overrides: - path: ../../../cookie_store/packages/cookie_store_conformance_tests - dynamite_runtime: - path: ../../../dynamite/packages/dynamite_runtime -+ frontend_server_client: ^4.0.0 - neon_lints: +@@ -10,3 +10,4 @@ dependency_overrides: path: ../../../neon_lints nextcloud: + path: ../../../nextcloud ++ frontend_server_client: ^4.0.0 diff --git a/packages/neon_framework/packages/notifications_app/pubspec.yaml b/packages/neon_framework/packages/notifications_app/pubspec.yaml --- a/packages/neon_framework/packages/notifications_app/pubspec.yaml +++ b/packages/neon_framework/packages/notifications_app/pubspec.yaml -@@ -44,6 +44,13 @@ dev_dependencies: +@@ -48,6 +48,13 @@ dev_dependencies: url_launcher_platform_interface: ^2.3.2 vector_graphics_compiler: ^1.1.16 - + +dependency_overrides: + archive: ^3.5.0 + image_picker: ^0.8.2 @@ -277,29 +288,20 @@ diff --git a/packages/neon_framework/packages/notifications_app/pubspec.yaml b/p diff --git a/packages/neon_framework/packages/notifications_app/pubspec_overrides.yaml b/packages/neon_framework/packages/notifications_app/pubspec_overrides.yaml --- a/packages/neon_framework/packages/notifications_app/pubspec_overrides.yaml +++ b/packages/neon_framework/packages/notifications_app/pubspec_overrides.yaml -@@ -1,11 +1,13 @@ --# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box -+# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box,archive,image_picker,platform,quiver,win32 +@@ -1,4 +1,4 @@ +-# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,neon_rich_text,nextcloud,notifications_push_repository,sort_box ++# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,neon_rich_text,nextcloud,notifications_push_repository,sort_box,archive,image_picker,platform,quiver,win32 dependency_overrides: account_repository: path: ../account_repository +@@ -22,3 +22,8 @@ dependency_overrides: + path: ../notifications_push_repository + sort_box: + path: ../sort_box + archive: ^3.5.0 - cookie_store: - path: ../../../cookie_store - dynamite_runtime: - path: ../../../dynamite/packages/dynamite_runtime + image_picker: ^0.8.2 - interceptor_http_client: - path: ../../../interceptor_http_client - neon_framework: -@@ -18,5 +20,8 @@ dependency_overrides: - path: ../../../nextcloud - notifications_push_repository: - path: ../notifications_push_repository + platform: ^3.1.0 + quiver: ^3.2.1 - sort_box: - path: ../sort_box + win32: ^5.5.1 diff --git a/packages/neon_framework/packages/notifications_push_repository/pubspec.yaml b/packages/neon_framework/packages/notifications_push_repository/pubspec.yaml --- a/packages/neon_framework/packages/notifications_push_repository/pubspec.yaml @@ -318,29 +320,20 @@ diff --git a/packages/neon_framework/packages/notifications_push_repository/pubs diff --git a/packages/neon_framework/packages/notifications_push_repository/pubspec_overrides.yaml b/packages/neon_framework/packages/notifications_push_repository/pubspec_overrides.yaml --- a/packages/neon_framework/packages/notifications_push_repository/pubspec_overrides.yaml +++ b/packages/neon_framework/packages/notifications_push_repository/pubspec_overrides.yaml -@@ -1,11 +1,13 @@ +@@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,sort_box +# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,sort_box,archive,image_picker,platform,quiver,win32 dependency_overrides: account_repository: path: ../account_repository +@@ -18,3 +18,8 @@ dependency_overrides: + path: ../../../nextcloud + sort_box: + path: ../sort_box + archive: ^3.5.0 - cookie_store: - path: ../../../cookie_store - dynamite_runtime: - path: ../../../dynamite/packages/dynamite_runtime + image_picker: ^0.8.2 - interceptor_http_client: - path: ../../../interceptor_http_client - neon_framework: -@@ -16,5 +18,8 @@ dependency_overrides: - path: ../../../neon_lints - nextcloud: - path: ../../../nextcloud + platform: ^3.1.0 + quiver: ^3.2.1 - sort_box: - path: ../sort_box + win32: ^5.5.1 diff --git a/packages/neon_framework/packages/sort_box/pubspec.yaml b/packages/neon_framework/packages/sort_box/pubspec.yaml --- a/packages/neon_framework/packages/sort_box/pubspec.yaml @@ -359,16 +352,16 @@ diff --git a/packages/neon_framework/packages/sort_box/pubspec_overrides.yaml b/ -# melos_managed_dependency_overrides: neon_lints +# melos_managed_dependency_overrides: neon_lints,frontend_server_client dependency_overrides: -+ frontend_server_client: ^4.0.0 neon_lints: path: ../../../neon_lints ++ frontend_server_client: ^4.0.0 diff --git a/packages/neon_framework/packages/talk_app/pubspec.yaml b/packages/neon_framework/packages/talk_app/pubspec.yaml --- a/packages/neon_framework/packages/talk_app/pubspec.yaml +++ b/packages/neon_framework/packages/talk_app/pubspec.yaml -@@ -48,6 +48,14 @@ dev_dependencies: +@@ -51,6 +51,14 @@ dev_dependencies: url_launcher_platform_interface: ^2.3.2 vector_graphics_compiler: ^1.1.16 - + +dependency_overrides: + archive: ^3.5.0 + image_picker: ^0.8.2 @@ -383,38 +376,29 @@ diff --git a/packages/neon_framework/packages/talk_app/pubspec.yaml b/packages/n diff --git a/packages/neon_framework/packages/talk_app/pubspec_overrides.yaml b/packages/neon_framework/packages/talk_app/pubspec_overrides.yaml --- a/packages/neon_framework/packages/talk_app/pubspec_overrides.yaml +++ b/packages/neon_framework/packages/talk_app/pubspec_overrides.yaml -@@ -1,11 +1,13 @@ --# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box -+# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box,archive,image_picker,platform,pointer_interceptor,quiver,win32 +@@ -1,4 +1,4 @@ +-# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,neon_rich_text,nextcloud,notifications_push_repository,sort_box ++# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,neon_rich_text,nextcloud,notifications_push_repository,sort_box,archive,image_picker,platform,pointer_interceptor,quiver,win32 dependency_overrides: account_repository: path: ../account_repository +@@ -22,3 +22,9 @@ dependency_overrides: + path: ../notifications_push_repository + sort_box: + path: ../sort_box + archive: ^3.5.0 - cookie_store: - path: ../../../cookie_store - dynamite_runtime: - path: ../../../dynamite/packages/dynamite_runtime + image_picker: ^0.8.2 - interceptor_http_client: - path: ../../../interceptor_http_client - neon_framework: -@@ -18,5 +20,9 @@ dependency_overrides: - path: ../../../nextcloud - notifications_push_repository: - path: ../notifications_push_repository + platform: ^3.1.0 + pointer_interceptor: ^0.10.1+1 + quiver: ^3.2.1 - sort_box: - path: ../sort_box + win32: ^5.5.1 diff --git a/packages/neon_framework/pubspec.yaml b/packages/neon_framework/pubspec.yaml --- a/packages/neon_framework/pubspec.yaml +++ b/packages/neon_framework/pubspec.yaml -@@ -111,6 +111,13 @@ dev_dependencies: +@@ -107,6 +107,13 @@ dev_dependencies: vector_graphics_compiler: ^1.1.16 version: ^3.0.2 - + +dependency_overrides: + archive: ^3.5.0 + image_picker: ^0.8.2 @@ -428,31 +412,20 @@ diff --git a/packages/neon_framework/pubspec.yaml b/packages/neon_framework/pubs diff --git a/packages/neon_framework/pubspec_overrides.yaml b/packages/neon_framework/pubspec_overrides.yaml --- a/packages/neon_framework/pubspec_overrides.yaml +++ b/packages/neon_framework/pubspec_overrides.yaml -@@ -1,13 +1,15 @@ +@@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: account_repository,cookie_store,cookie_store_conformance_tests,dynamite_runtime,interceptor_http_client,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box +# melos_managed_dependency_overrides: account_repository,cookie_store,cookie_store_conformance_tests,dynamite_runtime,interceptor_http_client,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box,archive,image_picker,platform,quiver,win32 dependency_overrides: account_repository: path: packages/account_repository +@@ -20,3 +20,8 @@ dependency_overrides: + path: packages/notifications_push_repository + sort_box: + path: packages/sort_box + archive: ^3.5.0 - cookie_store: - path: ../cookie_store - cookie_store_conformance_tests: - path: ../cookie_store/packages/cookie_store_conformance_tests - dynamite_runtime: - path: ../dynamite/packages/dynamite_runtime + image_picker: ^0.8.2 - interceptor_http_client: - path: ../interceptor_http_client - neon_http_client: -@@ -18,5 +20,8 @@ dependency_overrides: - path: ../nextcloud - notifications_push_repository: - path: packages/notifications_push_repository + platform: ^3.1.0 + quiver: ^3.2.1 - sort_box: - path: packages/sort_box + win32: ^5.5.1 diff --git a/packages/nextcloud/pubspec.yaml b/packages/nextcloud/pubspec.yaml --- a/packages/nextcloud/pubspec.yaml @@ -460,7 +433,7 @@ diff --git a/packages/nextcloud/pubspec.yaml b/packages/nextcloud/pubspec.yaml @@ -57,6 +57,10 @@ dev_dependencies: test_api: ^0.7.3 xml_serializable: ^2.5.3 - + +dependency_overrides: + frontend_server_client: ^4.0.0 + quiver: ^3.2.1 @@ -477,15 +450,9 @@ diff --git a/packages/nextcloud/pubspec_overrides.yaml b/packages/nextcloud/pubs dependency_overrides: cookie_store: path: ../cookie_store -@@ -6,9 +6,11 @@ dependency_overrides: - path: ../dynamite - dynamite_runtime: - path: ../dynamite/packages/dynamite_runtime -+ frontend_server_client: ^4.0.0 - interceptor_http_client: - path: ../interceptor_http_client - neon_lints: +@@ -12,3 +12,5 @@ dependency_overrides: path: ../neon_lints nextcloud_test: path: packages/nextcloud_test ++ frontend_server_client: ^4.0.0 + quiver: ^3.2.1 diff --git a/commitlint.yaml b/commitlint.yaml index 6618e1c9b04..9d24a3b4c16 100644 --- a/commitlint.yaml +++ b/commitlint.yaml @@ -25,6 +25,7 @@ rules: - neon_http_client - neon_lints - neon_lints_test + - neon_rich_text - neon_storage - news_app - nextcloud diff --git a/packages/neon_framework/example/pubspec.lock b/packages/neon_framework/example/pubspec.lock index 98fe0c0a452..1275c8ec49b 100644 --- a/packages/neon_framework/example/pubspec.lock +++ b/packages/neon_framework/example/pubspec.lock @@ -881,6 +881,13 @@ packages: relative: true source: path version: "1.0.0" + neon_rich_text: + dependency: "direct overridden" + description: + path: "../packages/neon_rich_text" + relative: true + source: path + version: "0.1.0" nested: dependency: transitive description: diff --git a/packages/neon_framework/example/pubspec_overrides.yaml b/packages/neon_framework/example/pubspec_overrides.yaml index 822df30a63b..0d99c7ffaa8 100644 --- a/packages/neon_framework/example/pubspec_overrides.yaml +++ b/packages/neon_framework/example/pubspec_overrides.yaml @@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: account_repository,cookie_store,dashboard_app,dynamite_runtime,files_app,interceptor_http_client,neon_framework,neon_http_client,neon_lints,news_app,nextcloud,notes_app,notifications_app,notifications_push_repository,sort_box,talk_app +# melos_managed_dependency_overrides: account_repository,cookie_store,dashboard_app,dynamite_runtime,files_app,interceptor_http_client,neon_framework,neon_http_client,neon_lints,neon_rich_text,news_app,nextcloud,notes_app,notifications_app,notifications_push_repository,sort_box,talk_app dependency_overrides: account_repository: path: ../packages/account_repository @@ -18,6 +18,8 @@ dependency_overrides: path: ../packages/neon_http_client neon_lints: path: ../../neon_lints + neon_rich_text: + path: ../packages/neon_rich_text news_app: path: ../packages/news_app nextcloud: diff --git a/packages/neon_framework/lib/widgets.dart b/packages/neon_framework/lib/widgets.dart index ba4913ca3a8..7813ed5e50d 100644 --- a/packages/neon_framework/lib/widgets.dart +++ b/packages/neon_framework/lib/widgets.dart @@ -7,7 +7,6 @@ export 'package:neon_framework/src/widgets/image.dart' hide NeonImage; export 'package:neon_framework/src/widgets/linear_progress_indicator.dart'; export 'package:neon_framework/src/widgets/list_view.dart'; export 'package:neon_framework/src/widgets/relative_time.dart'; -export 'package:neon_framework/src/widgets/rich_text/rich_text.dart'; export 'package:neon_framework/src/widgets/server_icon.dart'; export 'package:neon_framework/src/widgets/user_avatar.dart' hide NeonUserStatusIndicator; export 'package:neon_framework/src/widgets/user_status_icon.dart'; diff --git a/packages/neon_framework/packages/neon_rich_text/LICENSE b/packages/neon_framework/packages/neon_rich_text/LICENSE new file mode 120000 index 00000000000..f0b83dad961 --- /dev/null +++ b/packages/neon_framework/packages/neon_rich_text/LICENSE @@ -0,0 +1 @@ +../../../../assets/AGPL-3.0.txt \ No newline at end of file diff --git a/packages/neon_framework/packages/neon_rich_text/analysis_options.yaml b/packages/neon_framework/packages/neon_rich_text/analysis_options.yaml new file mode 100644 index 00000000000..d8df516716f --- /dev/null +++ b/packages/neon_framework/packages/neon_rich_text/analysis_options.yaml @@ -0,0 +1,5 @@ +include: package:neon_lints/flutter.yaml + +custom_lint: + rules: + - avoid_exports: false diff --git a/packages/neon_framework/packages/neon_rich_text/lib/neon_rich_text.dart b/packages/neon_framework/packages/neon_rich_text/lib/neon_rich_text.dart new file mode 100644 index 00000000000..7623bbc4cb4 --- /dev/null +++ b/packages/neon_framework/packages/neon_rich_text/lib/neon_rich_text.dart @@ -0,0 +1,5 @@ +export 'src/rich_objects/deck_card.dart'; +export 'src/rich_objects/fallback.dart'; +export 'src/rich_objects/file.dart'; +export 'src/rich_objects/mention.dart'; +export 'src/rich_text.dart'; diff --git a/packages/neon_framework/lib/src/widgets/rich_text/deck_card.dart b/packages/neon_framework/packages/neon_rich_text/lib/src/rich_objects/deck_card.dart similarity index 100% rename from packages/neon_framework/lib/src/widgets/rich_text/deck_card.dart rename to packages/neon_framework/packages/neon_rich_text/lib/src/rich_objects/deck_card.dart diff --git a/packages/neon_framework/lib/src/widgets/rich_text/fallback.dart b/packages/neon_framework/packages/neon_rich_text/lib/src/rich_objects/fallback.dart similarity index 100% rename from packages/neon_framework/lib/src/widgets/rich_text/fallback.dart rename to packages/neon_framework/packages/neon_rich_text/lib/src/rich_objects/fallback.dart diff --git a/packages/neon_framework/lib/src/widgets/rich_text/file.dart b/packages/neon_framework/packages/neon_rich_text/lib/src/rich_objects/file.dart similarity index 100% rename from packages/neon_framework/lib/src/widgets/rich_text/file.dart rename to packages/neon_framework/packages/neon_rich_text/lib/src/rich_objects/file.dart diff --git a/packages/neon_framework/lib/src/widgets/rich_text/mention.dart b/packages/neon_framework/packages/neon_rich_text/lib/src/rich_objects/mention.dart similarity index 100% rename from packages/neon_framework/lib/src/widgets/rich_text/mention.dart rename to packages/neon_framework/packages/neon_rich_text/lib/src/rich_objects/mention.dart diff --git a/packages/neon_framework/packages/neon_rich_text/lib/src/rich_objects/rich_objects.dart b/packages/neon_framework/packages/neon_rich_text/lib/src/rich_objects/rich_objects.dart new file mode 100644 index 00000000000..017f29e37a8 --- /dev/null +++ b/packages/neon_framework/packages/neon_rich_text/lib/src/rich_objects/rich_objects.dart @@ -0,0 +1,47 @@ +import 'package:flutter/widgets.dart'; +import 'package:meta/meta.dart'; +import 'package:neon_rich_text/src/rich_objects/deck_card.dart'; +import 'package:neon_rich_text/src/rich_objects/fallback.dart'; +import 'package:neon_rich_text/src/rich_objects/file.dart'; +import 'package:neon_rich_text/src/rich_objects/mention.dart'; +import 'package:nextcloud/core.dart' as core; + +/// Renders a rich object [parameter] to be interactive. +@internal +InlineSpan buildRichObjectParameter({ + required core.RichObjectParameter parameter, + required TextStyle? textStyle, + required bool isPreview, +}) { + if (isPreview) { + return TextSpan( + text: parameter.name, + style: textStyle, + ); + } + + return WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: switch (parameter.type) { + core.RichObjectParameter_Type.user || + core.RichObjectParameter_Type.call || + core.RichObjectParameter_Type.guest || + core.RichObjectParameter_Type.userGroup => + NeonRichObjectMention( + parameter: parameter, + textStyle: textStyle, + ), + core.RichObjectParameter_Type.file => NeonRichObjectFile( + parameter: parameter, + textStyle: textStyle, + ), + core.RichObjectParameter_Type.deckCard => NeonRichObjectDeckCard( + parameter: parameter, + ), + _ => NeonRichObjectFallback( + parameter: parameter, + textStyle: textStyle, + ), + }, + ); +} diff --git a/packages/neon_framework/lib/src/widgets/rich_text/rich_text.dart b/packages/neon_framework/packages/neon_rich_text/lib/src/rich_text.dart similarity index 65% rename from packages/neon_framework/lib/src/widgets/rich_text/rich_text.dart rename to packages/neon_framework/packages/neon_rich_text/lib/src/rich_text.dart index e4263c229b6..a939e9759ae 100644 --- a/packages/neon_framework/lib/src/widgets/rich_text/rich_text.dart +++ b/packages/neon_framework/packages/neon_rich_text/lib/src/rich_text.dart @@ -3,17 +3,9 @@ import 'package:built_value/json_object.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/widgets.dart'; import 'package:intersperse/intersperse.dart'; -import 'package:neon_framework/src/widgets/rich_text/deck_card.dart'; -import 'package:neon_framework/src/widgets/rich_text/fallback.dart'; -import 'package:neon_framework/src/widgets/rich_text/file.dart'; -import 'package:neon_framework/src/widgets/rich_text/mention.dart'; +import 'package:neon_rich_text/src/rich_objects/rich_objects.dart'; import 'package:nextcloud/core.dart' as core; -export 'package:neon_framework/src/widgets/rich_text/deck_card.dart'; -export 'package:neon_framework/src/widgets/rich_text/fallback.dart'; -export 'package:neon_framework/src/widgets/rich_text/file.dart'; -export 'package:neon_framework/src/widgets/rich_text/mention.dart'; - /// Renders the [text] as a rich [TextSpan]. TextSpan buildRichTextSpan({ required String text, @@ -132,42 +124,3 @@ TextSpan buildRichTextSpan({ children: children, ); } - -/// Renders a rich object [parameter] to be interactive. -InlineSpan buildRichObjectParameter({ - required core.RichObjectParameter parameter, - required TextStyle? textStyle, - required bool isPreview, -}) { - if (isPreview) { - return TextSpan( - text: parameter.name, - style: textStyle, - ); - } - - return WidgetSpan( - alignment: PlaceholderAlignment.middle, - child: switch (parameter.type) { - core.RichObjectParameter_Type.user || - core.RichObjectParameter_Type.call || - core.RichObjectParameter_Type.guest || - core.RichObjectParameter_Type.userGroup => - NeonRichObjectMention( - parameter: parameter, - textStyle: textStyle, - ), - core.RichObjectParameter_Type.file => NeonRichObjectFile( - parameter: parameter, - textStyle: textStyle, - ), - core.RichObjectParameter_Type.deckCard => NeonRichObjectDeckCard( - parameter: parameter, - ), - _ => NeonRichObjectFallback( - parameter: parameter, - textStyle: textStyle, - ), - }, - ); -} diff --git a/packages/neon_framework/packages/neon_rich_text/pubspec.yaml b/packages/neon_framework/packages/neon_rich_text/pubspec.yaml new file mode 100644 index 00000000000..1b29d51e1de --- /dev/null +++ b/packages/neon_framework/packages/neon_rich_text/pubspec.yaml @@ -0,0 +1,38 @@ +name: neon_rich_text +description: A package for rendering rich text from Nextcloud +version: 0.1.0 +publish_to: none + +environment: + sdk: ^3.0.0 + flutter: ^3.22.0 + +dependencies: + built_collection: ^5.0.0 + built_value: ^8.9.0 + flutter: + sdk: flutter + flutter_material_design_icons: ^1.1.7296 + intersperse: ^2.0.0 + meta: ^1.0.0 + neon_framework: + git: + url: https://github.com/nextcloud/neon + path: packages/neon_framework + nextcloud: ^8.1.0 + +dev_dependencies: + custom_lint: ^0.7.0 + flutter_test: + sdk: flutter + mocktail: ^1.0.4 + neon_lints: + git: + url: https://github.com/nextcloud/neon + path: packages/neon_lints + provider: ^6.1.2 + rxdart: ^0.28.0 + url_launcher_platform_interface: ^2.3.2 + +flutter: + uses-material-design: true diff --git a/packages/neon_framework/packages/neon_rich_text/pubspec_overrides.yaml b/packages/neon_framework/packages/neon_rich_text/pubspec_overrides.yaml new file mode 100644 index 00000000000..424806f913b --- /dev/null +++ b/packages/neon_framework/packages/neon_rich_text/pubspec_overrides.yaml @@ -0,0 +1,22 @@ +# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box +dependency_overrides: + account_repository: + path: ../account_repository + cookie_store: + path: ../../../cookie_store + dynamite_runtime: + path: ../../../dynamite/packages/dynamite_runtime + interceptor_http_client: + path: ../../../interceptor_http_client + neon_framework: + path: ../.. + neon_http_client: + path: ../neon_http_client + neon_lints: + path: ../../../neon_lints + nextcloud: + path: ../../../nextcloud + notifications_push_repository: + path: ../notifications_push_repository + sort_box: + path: ../sort_box diff --git a/packages/neon_framework/packages/neon_rich_text/test/rich_objects/deck_card_test.dart b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/deck_card_test.dart new file mode 100644 index 00000000000..fbd8d5bf54f --- /dev/null +++ b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/deck_card_test.dart @@ -0,0 +1,59 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:neon_framework/models.dart'; +import 'package:neon_framework/testing.dart'; +import 'package:neon_rich_text/neon_rich_text.dart'; +import 'package:nextcloud/core.dart' as core; +import 'package:provider/provider.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +void main() { + late MockUrlLauncher urlLauncher; + late Account account; + + setUpAll(() { + FakeNeonStorage.setup(); + + registerFallbackValue(const LaunchOptions()); + + urlLauncher = MockUrlLauncher(); + // ignore: discarded_futures + when(() => urlLauncher.launchUrl(any(), any())).thenAnswer((_) async => true); + + UrlLauncherPlatform.instance = urlLauncher; + }); + + setUp(() { + account = MockAccount(); + }); + + testWidgets('Deck card', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + ], + child: NeonRichObjectDeckCard( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.deckCard + ..id = '' + ..name = 'name' + ..boardname = 'boardname' + ..stackname = 'stackname' + ..link = '/link', + ), + ), + ), + ); + expect(find.text('name'), findsOne); + expect(find.text('boardname: stackname'), findsOne); + await expectLater( + find.byType(NeonRichObjectDeckCard), + matchesGoldenFile('goldens/rich_text_object_deck_card.png'), + ); + + await tester.tap(find.byType(NeonRichObjectDeckCard)); + verify(() => urlLauncher.launchUrl('https://cloud.example.com:8443/link', any())).called(1); + }); +} diff --git a/packages/neon_framework/packages/neon_rich_text/test/rich_objects/fallback_test.dart b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/fallback_test.dart new file mode 100644 index 00000000000..bcf7b2bacc8 --- /dev/null +++ b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/fallback_test.dart @@ -0,0 +1,103 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:neon_framework/models.dart'; +import 'package:neon_framework/testing.dart'; +import 'package:neon_framework/widgets.dart'; +import 'package:neon_rich_text/neon_rich_text.dart'; +import 'package:nextcloud/core.dart' as core; +import 'package:provider/provider.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +void main() { + late MockUrlLauncher urlLauncher; + late Account account; + + setUpAll(() { + FakeNeonStorage.setup(); + + registerFallbackValue(const LaunchOptions()); + + urlLauncher = MockUrlLauncher(); + // ignore: discarded_futures + when(() => urlLauncher.launchUrl(any(), any())).thenAnswer((_) async => true); + + UrlLauncherPlatform.instance = urlLauncher; + }); + + setUp(() { + account = MockAccount(); + }); + + group('Fallback', () { + testWidgets('Opens link', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + ], + child: NeonRichObjectFallback( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.calendarEvent + ..id = '' + ..name = 'name' + ..link = '/link', + ), + textStyle: null, + ), + ), + ); + + await tester.tap(find.byType(NeonRichObjectFallback)); + verify(() => urlLauncher.launchUrl('https://cloud.example.com:8443/link', any())).called(1); + }); + + testWidgets('Without icon', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + child: NeonRichObjectFallback( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.addressbook + ..id = '' + ..name = 'name', + ), + textStyle: null, + ), + ), + ); + expect(find.byType(NeonUriImage), findsNothing); + expect(find.text('name'), findsOne); + await expectLater( + find.byType(NeonRichObjectFallback), + matchesGoldenFile('goldens/rich_text_object_fallback_without_icon.png'), + ); + }); + + testWidgets('With icon', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + ], + child: NeonRichObjectFallback( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.addressbook + ..id = '' + ..name = 'name' + ..iconUrl = '', + ), + textStyle: null, + ), + ), + ); + expect(find.byType(NeonUriImage), findsOne); + expect(find.text('name'), findsOne); + await expectLater( + find.byType(NeonRichObjectFallback), + matchesGoldenFile('goldens/rich_text_object_fallback_with_icon.png'), + ); + }); + }); +} diff --git a/packages/neon_framework/packages/neon_rich_text/test/rich_objects/file_test.dart b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/file_test.dart new file mode 100644 index 00000000000..d793adf6458 --- /dev/null +++ b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/file_test.dart @@ -0,0 +1,253 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:neon_framework/models.dart'; +import 'package:neon_framework/testing.dart'; +import 'package:neon_framework/widgets.dart'; +import 'package:neon_rich_text/neon_rich_text.dart'; +import 'package:nextcloud/core.dart' as core; +import 'package:provider/provider.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +const validBlurHash = 'LEHLk~WB2yk8pyo0adR*.7kCMdnj'; + +void main() { + late MockUrlLauncher urlLauncher; + late Account account; + + setUpAll(() { + FakeNeonStorage.setup(); + + registerFallbackValue(const LaunchOptions()); + + urlLauncher = MockUrlLauncher(); + // ignore: discarded_futures + when(() => urlLauncher.launchUrl(any(), any())).thenAnswer((_) async => true); + + UrlLauncherPlatform.instance = urlLauncher; + }); + + setUp(() { + account = MockAccount(); + }); + + group('File', () { + const pixelRatio = 3; + const maxWidth = 800; + const maxHeight = 600 ~/ 2; + + testWidgets('Opens link', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + ], + child: NeonRichObjectFile( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.file + ..id = '0' + ..name = 'name' + ..previewAvailable = 'no' + ..path = '' + ..link = '/link', + ), + textStyle: null, + ), + ), + ); + + await tester.tap(find.byType(NeonRichObjectFile)); + verify(() => urlLauncher.launchUrl('https://cloud.example.com:8443/link', any())).called(1); + }); + + testWidgets('With blurhash', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + ], + child: NeonRichObjectFile( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.file + ..id = '0' + ..name = 'name' + ..previewAvailable = 'no' + ..path = '' + ..blurhash = validBlurHash, + ), + textStyle: null, + ), + ), + ); + + expect( + find.byWidgetPredicate( + (widget) => widget is NeonApiImage && widget.blurHash == validBlurHash, + ), + findsOne, + ); + }); + + testWidgets('Without preview', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + ], + child: NeonRichObjectFile( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.file + ..id = '0' + ..name = 'name' + ..previewAvailable = 'no' + ..path = 'path', + ), + textStyle: null, + ), + ), + ); + + expect(find.byTooltip('name'), findsNothing); + expect(find.text('name'), findsOne); + }); + + testWidgets('Without dimensions', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + ], + child: NeonRichObjectFile( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.file + ..id = '0' + ..name = 'name' + ..previewAvailable = 'yes' + ..path = 'path', + ), + textStyle: null, + ), + ), + ); + + final expectedConstraints = BoxConstraints( + minHeight: 100, + maxHeight: maxHeight.toDouble(), + minWidth: 100, + maxWidth: maxWidth.toDouble(), + ); + expect( + find.byWidgetPredicate((widget) => widget is ConstrainedBox && widget.constraints == expectedConstraints), + findsOne, + ); + expect(find.byTooltip('name'), findsOne); + }); + + testWidgets('With dimensions', (tester) async { + const width = 900; + const height = 300; + + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + ], + child: NeonRichObjectFile( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.file + ..id = '0' + ..name = 'name' + ..previewAvailable = 'yes' + ..path = 'path' + ..width = width.toString() + ..height = height.toString(), + ), + textStyle: null, + ), + ), + ); + + final expectedConstraints = BoxConstraints.tight(const Size(width / pixelRatio, height / pixelRatio)); + expect( + find.byWidgetPredicate((widget) => widget is ConstrainedBox && widget.constraints == expectedConstraints), + findsOne, + ); + expect(find.byTooltip('name'), findsOne); + }); + + testWidgets('With dimensions too big', (tester) async { + const widthFactor = 2; // Make it too big + const heightFactor = 4; // Make this even bigger + + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + ], + child: NeonRichObjectFile( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.file + ..id = '0' + ..name = 'name' + ..previewAvailable = 'yes' + ..path = 'path' + ..width = ((maxWidth * widthFactor) * pixelRatio).toString() + ..height = ((maxHeight * heightFactor) * pixelRatio).toString(), + ), + textStyle: null, + ), + ), + ); + + final size = Size( + widthFactor * maxWidth / heightFactor, + maxHeight.toDouble(), + ); + final expectedConstraints = BoxConstraints.tight(size); + expect( + find.byWidgetPredicate((widget) => widget is ConstrainedBox && widget.constraints == expectedConstraints), + findsOne, + ); + expect(find.byTooltip('name'), findsOne); + }); + + testWidgets('Full image for animated GIF', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + ], + child: NeonRichObjectFile( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.file + ..id = '0' + ..name = 'name' + ..previewAvailable = 'yes' + ..path = 'path' + ..mimetype = 'image/gif' + ..blurhash = validBlurHash, + ), + textStyle: null, + ), + ), + ); + + expect( + find.byWidgetPredicate( + (widget) => + widget is NeonUriImage && + widget.uri.toString() == 'https://cloud.example.com:8443/nextcloud/remote.php/dav/files/username/path' && + widget.blurHash == validBlurHash, + ), + findsOne, + ); + }); + }); +} diff --git a/packages/neon_framework/test/goldens/rich_text_object_deck_card.png b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_deck_card.png similarity index 100% rename from packages/neon_framework/test/goldens/rich_text_object_deck_card.png rename to packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_deck_card.png diff --git a/packages/neon_framework/test/goldens/rich_text_object_fallback_with_icon.png b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_fallback_with_icon.png similarity index 100% rename from packages/neon_framework/test/goldens/rich_text_object_fallback_with_icon.png rename to packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_fallback_with_icon.png diff --git a/packages/neon_framework/test/goldens/rich_text_object_fallback_without_icon.png b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_fallback_without_icon.png similarity index 100% rename from packages/neon_framework/test/goldens/rich_text_object_fallback_without_icon.png rename to packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_fallback_without_icon.png diff --git a/packages/neon_framework/test/goldens/rich_text_object_mention_call.png b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_mention_call.png similarity index 100% rename from packages/neon_framework/test/goldens/rich_text_object_mention_call.png rename to packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_mention_call.png diff --git a/packages/neon_framework/test/goldens/rich_text_object_mention_guest.png b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_mention_guest.png similarity index 100% rename from packages/neon_framework/test/goldens/rich_text_object_mention_guest.png rename to packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_mention_guest.png diff --git a/packages/neon_framework/test/goldens/rich_text_object_mention_user-group_highlight.png b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_mention_user-group_highlight.png similarity index 100% rename from packages/neon_framework/test/goldens/rich_text_object_mention_user-group_highlight.png rename to packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_mention_user-group_highlight.png diff --git a/packages/neon_framework/test/goldens/rich_text_object_mention_user-group_other.png b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_mention_user-group_other.png similarity index 100% rename from packages/neon_framework/test/goldens/rich_text_object_mention_user-group_other.png rename to packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_mention_user-group_other.png diff --git a/packages/neon_framework/test/goldens/rich_text_object_mention_user_highlight.png b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_mention_user_highlight.png similarity index 100% rename from packages/neon_framework/test/goldens/rich_text_object_mention_user_highlight.png rename to packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_mention_user_highlight.png diff --git a/packages/neon_framework/test/goldens/rich_text_object_mention_user_other.png b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_mention_user_other.png similarity index 100% rename from packages/neon_framework/test/goldens/rich_text_object_mention_user_other.png rename to packages/neon_framework/packages/neon_rich_text/test/rich_objects/goldens/rich_text_object_mention_user_other.png diff --git a/packages/neon_framework/packages/neon_rich_text/test/rich_objects/mention_test.dart b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/mention_test.dart new file mode 100644 index 00000000000..8dab2fd598e --- /dev/null +++ b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/mention_test.dart @@ -0,0 +1,181 @@ +import 'package:built_collection/built_collection.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:neon_framework/blocs.dart'; +import 'package:neon_framework/models.dart'; +import 'package:neon_framework/testing.dart'; +import 'package:neon_framework/theme.dart'; +import 'package:neon_framework/utils.dart'; +import 'package:neon_framework/widgets.dart'; +import 'package:neon_rich_text/neon_rich_text.dart'; +import 'package:nextcloud/core.dart' as core; +import 'package:provider/provider.dart'; +import 'package:rxdart/rxdart.dart'; + +void main() { + late Account account; + + setUpAll(() { + FakeNeonStorage.setup(); + }); + + setUp(() { + account = MockAccount(); + }); + + group('Mention', () { + group('user', () { + testWidgets('Self', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + ], + child: NeonRichObjectMention( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.user + ..id = 'username' + ..name = 'name', + ), + textStyle: null, + ), + ), + ); + expect(find.byType(NeonUserAvatar), findsOne); + expect(find.text('name'), findsOne); + await expectLater( + find.byType(NeonRichObjectMention), + matchesGoldenFile('goldens/rich_text_object_mention_user_highlight.png'), + ); + }); + + testWidgets('Other', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + ], + child: NeonRichObjectMention( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.user + ..id = 'other' + ..name = 'name', + ), + textStyle: null, + ), + ), + ); + expect(find.byType(NeonUserAvatar), findsOne); + expect(find.text('name'), findsOne); + await expectLater( + find.byType(NeonRichObjectMention), + matchesGoldenFile('goldens/rich_text_object_mention_user_other.png'), + ); + }); + }); + + testWidgets('call', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + ], + child: NeonRichObjectMention( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.call + ..id = '' + ..name = 'name' + ..iconUrl = '', + ), + textStyle: null, + ), + ), + ); + expect(find.byType(NeonUriImage), findsOne); + expect(find.text('name'), findsOne); + await expectLater( + find.byType(NeonRichObjectMention), + matchesGoldenFile('goldens/rich_text_object_mention_call.png'), + ); + }); + + testWidgets('guest', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + child: NeonRichObjectMention( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.guest + ..id = '' + ..name = 'name', + ), + textStyle: null, + ), + ), + ); + expect(find.byIcon(AdaptiveIcons.person), findsOne); + expect(find.text('name'), findsOne); + await expectLater( + find.byType(NeonRichObjectMention), + matchesGoldenFile('goldens/rich_text_object_mention_guest.png'), + ); + }); + + testWidgets('user-group', (tester) async { + final userDetails = MockUserDetails(); + when(() => userDetails.groups).thenReturn(BuiltList(['group'])); + + final userDetailsBloc = MockUserDetailsBloc(); + when(() => userDetailsBloc.userDetails).thenAnswer((_) => BehaviorSubject.seeded(Result.success(userDetails))); + + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + NeonProvider.value(value: userDetailsBloc), + ], + child: NeonRichObjectMention( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.userGroup + ..id = 'group' + ..name = 'name', + ), + textStyle: null, + ), + ), + ); + expect(find.byIcon(AdaptiveIcons.group), findsOne); + expect(find.text('name'), findsOne); + await expectLater( + find.byType(NeonRichObjectMention), + matchesGoldenFile('goldens/rich_text_object_mention_user-group_highlight.png'), + ); + + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + NeonProvider.value(value: userDetailsBloc), + ], + child: NeonRichObjectMention( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.userGroup + ..id = 'other' + ..name = 'name', + ), + textStyle: null, + ), + ), + ); + expect(find.byIcon(AdaptiveIcons.group), findsOne); + expect(find.text('name'), findsOne); + await expectLater( + find.byType(NeonRichObjectMention), + matchesGoldenFile('goldens/rich_text_object_mention_user-group_other.png'), + ); + }); + }); +} diff --git a/packages/neon_framework/packages/neon_rich_text/test/rich_objects/rich_objects_test.dart b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/rich_objects_test.dart new file mode 100644 index 00000000000..bd1a4beebb3 --- /dev/null +++ b/packages/neon_framework/packages/neon_rich_text/test/rich_objects/rich_objects_test.dart @@ -0,0 +1,143 @@ +import 'package:built_collection/built_collection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:neon_framework/blocs.dart'; +import 'package:neon_framework/models.dart'; +import 'package:neon_framework/testing.dart'; +import 'package:neon_framework/utils.dart'; +import 'package:neon_rich_text/neon_rich_text.dart'; +import 'package:neon_rich_text/src/rich_objects/rich_objects.dart'; +import 'package:nextcloud/core.dart' as core; +import 'package:provider/provider.dart'; +import 'package:rxdart/rxdart.dart'; + +void main() { + setUpAll(() { + FakeNeonStorage.setup(); + }); + + group('buildRichObjectParameter', () { + for (final isPreview in [true, false]) { + group(isPreview ? 'As preview' : 'Complete', () { + group('Mention', () { + for (final type in [ + core.RichObjectParameter_Type.user, + core.RichObjectParameter_Type.call, + core.RichObjectParameter_Type.guest, + core.RichObjectParameter_Type.userGroup, + ]) { + testWidgets(type.value, (tester) async { + final userDetails = MockUserDetails(); + when(() => userDetails.groups).thenReturn(BuiltList()); + + final userDetailsBloc = MockUserDetailsBloc(); + when(() => userDetailsBloc.userDetails) + .thenAnswer((_) => BehaviorSubject.seeded(Result.success(userDetails))); + + final account = MockAccount(); + + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + NeonProvider.value(value: userDetailsBloc), + ], + child: RichText( + text: buildRichObjectParameter( + parameter: core.RichObjectParameter( + (b) => b + ..type = type + ..id = '' + ..name = 'name' + ..iconUrl = '', + ), + textStyle: null, + isPreview: isPreview, + ), + ), + ), + ); + + expect(find.byType(NeonRichObjectMention), isPreview ? findsNothing : findsOne); + expect(find.text('name', findRichText: true), findsOne); + }); + } + }); + + testWidgets('File', (tester) async { + final account = MockAccount(); + + await tester.pumpWidgetWithAccessibility( + TestApp( + providers: [ + Provider.value(value: account), + ], + child: RichText( + text: buildRichObjectParameter( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.file + ..id = '0' + ..name = 'name', + ), + textStyle: null, + isPreview: isPreview, + ), + ), + ), + ); + + expect(find.byType(NeonRichObjectFile), isPreview ? findsNothing : findsOne); + expect(find.text('name', findRichText: true), findsOne); + }); + + testWidgets('Deck card', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + child: RichText( + text: buildRichObjectParameter( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.deckCard + ..id = '' + ..name = 'name' + ..boardname = 'boardname' + ..stackname = 'stackname', + ), + textStyle: null, + isPreview: isPreview, + ), + ), + ), + ); + + expect(find.byType(NeonRichObjectDeckCard), isPreview ? findsNothing : findsOne); + expect(find.text('name', findRichText: true), findsOne); + }); + + testWidgets('Fallback', (tester) async { + await tester.pumpWidgetWithAccessibility( + TestApp( + child: RichText( + text: buildRichObjectParameter( + parameter: core.RichObjectParameter( + (b) => b + ..type = core.RichObjectParameter_Type.addressbook + ..id = '' + ..name = 'name', + ), + textStyle: null, + isPreview: isPreview, + ), + ), + ), + ); + + expect(find.byType(NeonRichObjectFallback), isPreview ? findsNothing : findsOne); + expect(find.text('name', findRichText: true), findsOne); + }); + }); + } + }); +} diff --git a/packages/neon_framework/packages/neon_rich_text/test/rich_text_test.dart b/packages/neon_framework/packages/neon_rich_text/test/rich_text_test.dart new file mode 100644 index 00000000000..38bb8d85eeb --- /dev/null +++ b/packages/neon_framework/packages/neon_rich_text/test/rich_text_test.dart @@ -0,0 +1,139 @@ +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/json_object.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:neon_rich_text/neon_rich_text.dart'; +import 'package:nextcloud/core.dart' as core; + +class MockOnReferenceClickedCallback extends Mock { + void call(String reference); +} + +void main() { + group('buildRichTextSpan', () { + test('Preview without newlines', () { + var span = buildRichTextSpan( + text: '123\n456', + parameters: BuiltMap(), + references: BuiltList(), + style: const TextStyle(), + onReferenceClicked: (_) {}, + ).children!.single as TextSpan; + expect(span.text, '123\n456'); + + span = buildRichTextSpan( + text: '123\n456', + parameters: BuiltMap(), + references: BuiltList(), + style: const TextStyle(), + onReferenceClicked: (_) {}, + isPreview: true, + ).children!.single as TextSpan; + expect(span.text, '123 456'); + }); + + group('Unused parameters', () { + for (final type in core.RichObjectParameter_Type.values) { + test(type, () { + final spans = buildRichTextSpan( + text: 'test', + parameters: BuiltMap({ + type.value: BuiltMap({ + 'type': JsonObject(type.value), + 'id': JsonObject(1), + 'name': JsonObject(''), + }), + }), + references: BuiltList(), + style: const TextStyle(), + onReferenceClicked: (_) {}, + ).children!; + if (type == core.RichObjectParameter_Type.file) { + expect(spans, hasLength(3)); + expect((spans[0] as WidgetSpan).child, isA()); + expect((spans[1] as TextSpan).text, '\n'); + expect((spans[2] as TextSpan).text, 'test'); + } else { + expect((spans.single as TextSpan).text, 'test'); + } + }); + } + }); + + test('Used parameters', () { + final spans = buildRichTextSpan( + text: '123 {actor1} 456 {actor2} 789', + parameters: BuiltMap({ + 'actor1': BuiltMap({ + 'type': JsonObject('user'), + 'id': JsonObject(''), + 'name': JsonObject(''), + }), + 'actor2': BuiltMap({ + 'type': JsonObject('user'), + 'id': JsonObject(''), + 'name': JsonObject(''), + }), + }), + references: BuiltList(), + style: const TextStyle(), + onReferenceClicked: (_) {}, + ).children!; + expect(spans, hasLength(5)); + expect((spans[0] as TextSpan).text, '123 '); + expect((spans[1] as WidgetSpan).child, isA()); + expect((spans[2] as TextSpan).text, ' 456 '); + expect((spans[3] as WidgetSpan).child, isA()); + expect((spans[4] as TextSpan).text, ' 789'); + }); + + test('References', () { + final callback = MockOnReferenceClickedCallback(); + + final spans = buildRichTextSpan( + text: 'a 123 b 456 c', + parameters: BuiltMap(), + references: BuiltList(['123', '456']), + style: const TextStyle(), + onReferenceClicked: callback.call, + ).children!; + expect(spans, hasLength(5)); + + expect((spans[0] as TextSpan).text, 'a '); + + final link1 = spans[1] as TextSpan; + expect(link1.text, '123'); + (link1.recognizer! as TapGestureRecognizer).onTap!(); + verify(() => callback('123')).called(1); + + expect((spans[2] as TextSpan).text, ' b '); + + final link2 = spans[3] as TextSpan; + expect(link2.text, '456'); + (link2.recognizer! as TapGestureRecognizer).onTap!(); + verify(() => callback('456')).called(1); + + expect((spans[4] as TextSpan).text, ' c'); + }); + + test('Skip empty parts', () { + final spans = buildRichTextSpan( + text: '{actor}', + parameters: BuiltMap({ + 'actor': BuiltMap({ + 'type': JsonObject(core.RichObjectParameter_Type.user.name), + 'id': JsonObject(''), + 'name': JsonObject(''), + }), + }), + references: BuiltList(), + style: const TextStyle(), + onReferenceClicked: (_) {}, + ).children!; + expect(spans, hasLength(1)); + expect((spans[0] as WidgetSpan).child, isA()); + }); + }); +} diff --git a/packages/neon_framework/packages/notifications_app/lib/src/widgets/notification.dart b/packages/neon_framework/packages/notifications_app/lib/src/widgets/notification.dart index c5b79294bdf..95479353dc7 100644 --- a/packages/neon_framework/packages/notifications_app/lib/src/widgets/notification.dart +++ b/packages/neon_framework/packages/notifications_app/lib/src/widgets/notification.dart @@ -5,6 +5,7 @@ import 'package:neon_framework/models.dart'; import 'package:neon_framework/theme.dart'; import 'package:neon_framework/utils.dart'; import 'package:neon_framework/widgets.dart'; +import 'package:neon_rich_text/neon_rich_text.dart'; import 'package:nextcloud/notifications.dart' as notifications; import 'package:notifications_app/src/widgets/action.dart'; import 'package:timezone/timezone.dart' as tz; diff --git a/packages/neon_framework/packages/notifications_app/pubspec.yaml b/packages/neon_framework/packages/notifications_app/pubspec.yaml index 10dae34b15b..d4f9ed82af1 100644 --- a/packages/neon_framework/packages/notifications_app/pubspec.yaml +++ b/packages/neon_framework/packages/notifications_app/pubspec.yaml @@ -23,6 +23,10 @@ dependencies: git: url: https://github.com/nextcloud/neon path: packages/neon_framework + neon_rich_text: + git: + url: https://github.com/nextcloud/neon + path: packages/neon_framework/packages/neon_rich_text nextcloud: ^8.1.0 rxdart: ^0.28.0 timezone: ^0.9.4 diff --git a/packages/neon_framework/packages/notifications_app/pubspec_overrides.yaml b/packages/neon_framework/packages/notifications_app/pubspec_overrides.yaml index 424806f913b..c26f7798841 100644 --- a/packages/neon_framework/packages/notifications_app/pubspec_overrides.yaml +++ b/packages/neon_framework/packages/notifications_app/pubspec_overrides.yaml @@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box +# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,neon_rich_text,nextcloud,notifications_push_repository,sort_box dependency_overrides: account_repository: path: ../account_repository @@ -14,6 +14,8 @@ dependency_overrides: path: ../neon_http_client neon_lints: path: ../../../neon_lints + neon_rich_text: + path: ../neon_rich_text nextcloud: path: ../../../nextcloud notifications_push_repository: diff --git a/packages/neon_framework/packages/notifications_app/test/notification_test.dart b/packages/neon_framework/packages/notifications_app/test/notification_test.dart index eaabd3b3076..7c69315f343 100644 --- a/packages/neon_framework/packages/notifications_app/test/notification_test.dart +++ b/packages/neon_framework/packages/notifications_app/test/notification_test.dart @@ -5,6 +5,7 @@ import 'package:mocktail/mocktail.dart'; import 'package:neon_framework/models.dart'; import 'package:neon_framework/testing.dart'; import 'package:neon_framework/widgets.dart'; +import 'package:neon_rich_text/neon_rich_text.dart'; import 'package:nextcloud/notifications.dart' as notifications; import 'package:notifications_app/l10n/localizations.dart'; import 'package:notifications_app/src/widgets/action.dart'; diff --git a/packages/neon_framework/packages/talk_app/lib/src/widgets/message.dart b/packages/neon_framework/packages/talk_app/lib/src/widgets/message.dart index 9b937ba5fbf..d99d5dc2cd9 100644 --- a/packages/neon_framework/packages/talk_app/lib/src/widgets/message.dart +++ b/packages/neon_framework/packages/talk_app/lib/src/widgets/message.dart @@ -7,6 +7,7 @@ import 'package:neon_framework/models.dart'; import 'package:neon_framework/theme.dart'; import 'package:neon_framework/utils.dart'; import 'package:neon_framework/widgets.dart'; +import 'package:neon_rich_text/neon_rich_text.dart'; import 'package:nextcloud/spreed.dart' as spreed; import 'package:talk_app/l10n/localizations.dart'; import 'package:talk_app/src/blocs/message_bloc.dart'; diff --git a/packages/neon_framework/packages/talk_app/pubspec.yaml b/packages/neon_framework/packages/talk_app/pubspec.yaml index a4a376f4635..5462e009e68 100644 --- a/packages/neon_framework/packages/talk_app/pubspec.yaml +++ b/packages/neon_framework/packages/talk_app/pubspec.yaml @@ -25,6 +25,10 @@ dependencies: git: url: https://github.com/nextcloud/neon path: packages/neon_framework + neon_rich_text: + git: + url: https://github.com/nextcloud/neon + path: packages/neon_framework/packages/neon_rich_text nextcloud: ^8.1.0 rxdart: ^0.28.0 timezone: ^0.9.4 diff --git a/packages/neon_framework/packages/talk_app/pubspec_overrides.yaml b/packages/neon_framework/packages/talk_app/pubspec_overrides.yaml index 424806f913b..c26f7798841 100644 --- a/packages/neon_framework/packages/talk_app/pubspec_overrides.yaml +++ b/packages/neon_framework/packages/talk_app/pubspec_overrides.yaml @@ -1,4 +1,4 @@ -# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,nextcloud,notifications_push_repository,sort_box +# melos_managed_dependency_overrides: account_repository,cookie_store,dynamite_runtime,interceptor_http_client,neon_framework,neon_http_client,neon_lints,neon_rich_text,nextcloud,notifications_push_repository,sort_box dependency_overrides: account_repository: path: ../account_repository @@ -14,6 +14,8 @@ dependency_overrides: path: ../neon_http_client neon_lints: path: ../../../neon_lints + neon_rich_text: + path: ../neon_rich_text nextcloud: path: ../../../nextcloud notifications_push_repository: diff --git a/packages/neon_framework/test/rich_text_test.dart b/packages/neon_framework/test/rich_text_test.dart deleted file mode 100644 index 2a08bf2bffe..00000000000 --- a/packages/neon_framework/test/rich_text_test.dart +++ /dev/null @@ -1,771 +0,0 @@ -import 'package:built_collection/built_collection.dart'; -import 'package:built_value/json_object.dart'; -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; -import 'package:neon_framework/blocs.dart'; -import 'package:neon_framework/models.dart'; -import 'package:neon_framework/testing.dart'; -import 'package:neon_framework/theme.dart'; -import 'package:neon_framework/utils.dart'; -import 'package:neon_framework/widgets.dart'; -import 'package:nextcloud/core.dart' as core; -import 'package:provider/provider.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; - -const validBlurHash = 'LEHLk~WB2yk8pyo0adR*.7kCMdnj'; - -class MockOnReferenceClickedCallback extends Mock { - void call(String reference); -} - -void main() { - late MockUrlLauncher urlLauncher; - late Account account; - - setUpAll(() { - FakeNeonStorage.setup(); - - registerFallbackValue(const LaunchOptions()); - - urlLauncher = MockUrlLauncher(); - // ignore: discarded_futures - when(() => urlLauncher.launchUrl(any(), any())).thenAnswer((_) async => true); - - UrlLauncherPlatform.instance = urlLauncher; - }); - - setUp(() { - account = MockAccount(); - }); - - testWidgets('Deck card', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - ], - child: NeonRichObjectDeckCard( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.deckCard - ..id = '' - ..name = 'name' - ..boardname = 'boardname' - ..stackname = 'stackname' - ..link = '/link', - ), - ), - ), - ); - expect(find.text('name'), findsOne); - expect(find.text('boardname: stackname'), findsOne); - await expectLater( - find.byType(NeonRichObjectDeckCard), - matchesGoldenFile('goldens/rich_text_object_deck_card.png'), - ); - - await tester.tap(find.byType(NeonRichObjectDeckCard)); - verify(() => urlLauncher.launchUrl('https://cloud.example.com:8443/link', any())).called(1); - }); - - group('Mention', () { - group('user', () { - testWidgets('Self', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - ], - child: NeonRichObjectMention( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.user - ..id = 'username' - ..name = 'name', - ), - textStyle: null, - ), - ), - ); - expect(find.byType(NeonUserAvatar), findsOne); - expect(find.text('name'), findsOne); - await expectLater( - find.byType(NeonRichObjectMention), - matchesGoldenFile('goldens/rich_text_object_mention_user_highlight.png'), - ); - }); - - testWidgets('Other', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - ], - child: NeonRichObjectMention( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.user - ..id = 'other' - ..name = 'name', - ), - textStyle: null, - ), - ), - ); - expect(find.byType(NeonUserAvatar), findsOne); - expect(find.text('name'), findsOne); - await expectLater( - find.byType(NeonRichObjectMention), - matchesGoldenFile('goldens/rich_text_object_mention_user_other.png'), - ); - }); - }); - - testWidgets('call', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - ], - child: NeonRichObjectMention( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.call - ..id = '' - ..name = 'name' - ..iconUrl = '', - ), - textStyle: null, - ), - ), - ); - expect(find.byType(NeonUriImage), findsOne); - expect(find.text('name'), findsOne); - await expectLater( - find.byType(NeonRichObjectMention), - matchesGoldenFile('goldens/rich_text_object_mention_call.png'), - ); - }); - - testWidgets('guest', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - child: NeonRichObjectMention( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.guest - ..id = '' - ..name = 'name', - ), - textStyle: null, - ), - ), - ); - expect(find.byIcon(AdaptiveIcons.person), findsOne); - expect(find.text('name'), findsOne); - await expectLater( - find.byType(NeonRichObjectMention), - matchesGoldenFile('goldens/rich_text_object_mention_guest.png'), - ); - }); - - testWidgets('user-group', (tester) async { - final userDetails = MockUserDetails(); - when(() => userDetails.groups).thenReturn(BuiltList(['group'])); - - final userDetailsBloc = MockUserDetailsBloc(); - when(() => userDetailsBloc.userDetails).thenAnswer((_) => BehaviorSubject.seeded(Result.success(userDetails))); - - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - NeonProvider.value(value: userDetailsBloc), - ], - child: NeonRichObjectMention( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.userGroup - ..id = 'group' - ..name = 'name', - ), - textStyle: null, - ), - ), - ); - expect(find.byIcon(AdaptiveIcons.group), findsOne); - expect(find.text('name'), findsOne); - await expectLater( - find.byType(NeonRichObjectMention), - matchesGoldenFile('goldens/rich_text_object_mention_user-group_highlight.png'), - ); - - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - NeonProvider.value(value: userDetailsBloc), - ], - child: NeonRichObjectMention( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.userGroup - ..id = 'other' - ..name = 'name', - ), - textStyle: null, - ), - ), - ); - expect(find.byIcon(AdaptiveIcons.group), findsOne); - expect(find.text('name'), findsOne); - await expectLater( - find.byType(NeonRichObjectMention), - matchesGoldenFile('goldens/rich_text_object_mention_user-group_other.png'), - ); - }); - }); - - group('File', () { - const pixelRatio = 3; - const maxWidth = 800; - const maxHeight = 600 ~/ 2; - - testWidgets('Opens link', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - ], - child: NeonRichObjectFile( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.file - ..id = '0' - ..name = 'name' - ..previewAvailable = 'no' - ..path = '' - ..link = '/link', - ), - textStyle: null, - ), - ), - ); - - await tester.tap(find.byType(NeonRichObjectFile)); - verify(() => urlLauncher.launchUrl('https://cloud.example.com:8443/link', any())).called(1); - }); - - testWidgets('With blurhash', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - ], - child: NeonRichObjectFile( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.file - ..id = '0' - ..name = 'name' - ..previewAvailable = 'no' - ..path = '' - ..blurhash = validBlurHash, - ), - textStyle: null, - ), - ), - ); - - expect( - find.byWidgetPredicate( - (widget) => widget is NeonApiImage && widget.blurHash == validBlurHash, - ), - findsOne, - ); - }); - - testWidgets('Without preview', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - ], - child: NeonRichObjectFile( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.file - ..id = '0' - ..name = 'name' - ..previewAvailable = 'no' - ..path = 'path', - ), - textStyle: null, - ), - ), - ); - - expect(find.byTooltip('name'), findsNothing); - expect(find.text('name'), findsOne); - }); - - testWidgets('Without dimensions', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - ], - child: NeonRichObjectFile( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.file - ..id = '0' - ..name = 'name' - ..previewAvailable = 'yes' - ..path = 'path', - ), - textStyle: null, - ), - ), - ); - - final expectedConstraints = BoxConstraints( - minHeight: 100, - maxHeight: maxHeight.toDouble(), - minWidth: 100, - maxWidth: maxWidth.toDouble(), - ); - expect( - find.byWidgetPredicate((widget) => widget is ConstrainedBox && widget.constraints == expectedConstraints), - findsOne, - ); - expect(find.byTooltip('name'), findsOne); - }); - - testWidgets('With dimensions', (tester) async { - const width = 900; - const height = 300; - - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - ], - child: NeonRichObjectFile( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.file - ..id = '0' - ..name = 'name' - ..previewAvailable = 'yes' - ..path = 'path' - ..width = width.toString() - ..height = height.toString(), - ), - textStyle: null, - ), - ), - ); - - final expectedConstraints = BoxConstraints.tight(const Size(width / pixelRatio, height / pixelRatio)); - expect( - find.byWidgetPredicate((widget) => widget is ConstrainedBox && widget.constraints == expectedConstraints), - findsOne, - ); - expect(find.byTooltip('name'), findsOne); - }); - - testWidgets('With dimensions too big', (tester) async { - const widthFactor = 2; // Make it too big - const heightFactor = 4; // Make this even bigger - - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - ], - child: NeonRichObjectFile( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.file - ..id = '0' - ..name = 'name' - ..previewAvailable = 'yes' - ..path = 'path' - ..width = ((maxWidth * widthFactor) * pixelRatio).toString() - ..height = ((maxHeight * heightFactor) * pixelRatio).toString(), - ), - textStyle: null, - ), - ), - ); - - final size = Size( - widthFactor * maxWidth / heightFactor, - maxHeight.toDouble(), - ); - final expectedConstraints = BoxConstraints.tight(size); - expect( - find.byWidgetPredicate((widget) => widget is ConstrainedBox && widget.constraints == expectedConstraints), - findsOne, - ); - expect(find.byTooltip('name'), findsOne); - }); - - testWidgets('Full image for animated GIF', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - ], - child: NeonRichObjectFile( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.file - ..id = '0' - ..name = 'name' - ..previewAvailable = 'yes' - ..path = 'path' - ..mimetype = 'image/gif' - ..blurhash = validBlurHash, - ), - textStyle: null, - ), - ), - ); - - expect( - find.byWidgetPredicate( - (widget) => - widget is NeonUriImage && - widget.uri.toString() == 'https://cloud.example.com:8443/nextcloud/remote.php/dav/files/username/path' && - widget.blurHash == validBlurHash, - ), - findsOne, - ); - }); - }); - - group('Fallback', () { - testWidgets('Opens link', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - ], - child: NeonRichObjectFallback( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.calendarEvent - ..id = '' - ..name = 'name' - ..link = '/link', - ), - textStyle: null, - ), - ), - ); - - await tester.tap(find.byType(NeonRichObjectFallback)); - verify(() => urlLauncher.launchUrl('https://cloud.example.com:8443/link', any())).called(1); - }); - - testWidgets('Without icon', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - child: NeonRichObjectFallback( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.addressbook - ..id = '' - ..name = 'name', - ), - textStyle: null, - ), - ), - ); - expect(find.byType(NeonUriImage), findsNothing); - expect(find.text('name'), findsOne); - await expectLater( - find.byType(NeonRichObjectFallback), - matchesGoldenFile('goldens/rich_text_object_fallback_without_icon.png'), - ); - }); - - testWidgets('With icon', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - ], - child: NeonRichObjectFallback( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.addressbook - ..id = '' - ..name = 'name' - ..iconUrl = '', - ), - textStyle: null, - ), - ), - ); - expect(find.byType(NeonUriImage), findsOne); - expect(find.text('name'), findsOne); - await expectLater( - find.byType(NeonRichObjectFallback), - matchesGoldenFile('goldens/rich_text_object_fallback_with_icon.png'), - ); - }); - }); - - group('buildRichObjectParameter', () { - for (final isPreview in [true, false]) { - group(isPreview ? 'As preview' : 'Complete', () { - group('Mention', () { - for (final type in [ - core.RichObjectParameter_Type.user, - core.RichObjectParameter_Type.call, - core.RichObjectParameter_Type.guest, - core.RichObjectParameter_Type.userGroup, - ]) { - testWidgets(type.value, (tester) async { - final userDetails = MockUserDetails(); - when(() => userDetails.groups).thenReturn(BuiltList()); - - final userDetailsBloc = MockUserDetailsBloc(); - when(() => userDetailsBloc.userDetails) - .thenAnswer((_) => BehaviorSubject.seeded(Result.success(userDetails))); - - final account = MockAccount(); - - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - NeonProvider.value(value: userDetailsBloc), - ], - child: RichText( - text: buildRichObjectParameter( - parameter: core.RichObjectParameter( - (b) => b - ..type = type - ..id = '' - ..name = 'name' - ..iconUrl = '', - ), - textStyle: null, - isPreview: isPreview, - ), - ), - ), - ); - - expect(find.byType(NeonRichObjectMention), isPreview ? findsNothing : findsOne); - expect(find.text('name', findRichText: true), findsOne); - }); - } - }); - - testWidgets('File', (tester) async { - final account = MockAccount(); - - await tester.pumpWidgetWithAccessibility( - TestApp( - providers: [ - Provider.value(value: account), - ], - child: RichText( - text: buildRichObjectParameter( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.file - ..id = '0' - ..name = 'name', - ), - textStyle: null, - isPreview: isPreview, - ), - ), - ), - ); - - expect(find.byType(NeonRichObjectFile), isPreview ? findsNothing : findsOne); - expect(find.text('name', findRichText: true), findsOne); - }); - - testWidgets('Deck card', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - child: RichText( - text: buildRichObjectParameter( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.deckCard - ..id = '' - ..name = 'name' - ..boardname = 'boardname' - ..stackname = 'stackname', - ), - textStyle: null, - isPreview: isPreview, - ), - ), - ), - ); - - expect(find.byType(NeonRichObjectDeckCard), isPreview ? findsNothing : findsOne); - expect(find.text('name', findRichText: true), findsOne); - }); - - testWidgets('Fallback', (tester) async { - await tester.pumpWidgetWithAccessibility( - TestApp( - child: RichText( - text: buildRichObjectParameter( - parameter: core.RichObjectParameter( - (b) => b - ..type = core.RichObjectParameter_Type.addressbook - ..id = '' - ..name = 'name', - ), - textStyle: null, - isPreview: isPreview, - ), - ), - ), - ); - - expect(find.byType(NeonRichObjectFallback), isPreview ? findsNothing : findsOne); - expect(find.text('name', findRichText: true), findsOne); - }); - }); - } - }); - - group('buildRichTextSpan', () { - test('Preview without newlines', () { - var span = buildRichTextSpan( - text: '123\n456', - parameters: BuiltMap(), - references: BuiltList(), - style: const TextStyle(), - onReferenceClicked: (_) {}, - ).children!.single as TextSpan; - expect(span.text, '123\n456'); - - span = buildRichTextSpan( - text: '123\n456', - parameters: BuiltMap(), - references: BuiltList(), - style: const TextStyle(), - onReferenceClicked: (_) {}, - isPreview: true, - ).children!.single as TextSpan; - expect(span.text, '123 456'); - }); - - group('Unused parameters', () { - for (final type in core.RichObjectParameter_Type.values) { - test(type, () { - final spans = buildRichTextSpan( - text: 'test', - parameters: BuiltMap({ - type.value: BuiltMap({ - 'type': JsonObject(type.value), - 'id': JsonObject(1), - 'name': JsonObject(''), - }), - }), - references: BuiltList(), - style: const TextStyle(), - onReferenceClicked: (_) {}, - ).children!; - if (type == core.RichObjectParameter_Type.file) { - expect(spans, hasLength(3)); - expect((spans[0] as WidgetSpan).child, isA()); - expect((spans[1] as TextSpan).text, '\n'); - expect((spans[2] as TextSpan).text, 'test'); - } else { - expect((spans.single as TextSpan).text, 'test'); - } - }); - } - }); - - test('Used parameters', () { - final spans = buildRichTextSpan( - text: '123 {actor1} 456 {actor2} 789', - parameters: BuiltMap({ - 'actor1': BuiltMap({ - 'type': JsonObject('user'), - 'id': JsonObject(''), - 'name': JsonObject(''), - }), - 'actor2': BuiltMap({ - 'type': JsonObject('user'), - 'id': JsonObject(''), - 'name': JsonObject(''), - }), - }), - references: BuiltList(), - style: const TextStyle(), - onReferenceClicked: (_) {}, - ).children!; - expect(spans, hasLength(5)); - expect((spans[0] as TextSpan).text, '123 '); - expect((spans[1] as WidgetSpan).child, isA()); - expect((spans[2] as TextSpan).text, ' 456 '); - expect((spans[3] as WidgetSpan).child, isA()); - expect((spans[4] as TextSpan).text, ' 789'); - }); - - test('References', () { - final callback = MockOnReferenceClickedCallback(); - - final spans = buildRichTextSpan( - text: 'a 123 b 456 c', - parameters: BuiltMap(), - references: BuiltList(['123', '456']), - style: const TextStyle(), - onReferenceClicked: callback.call, - ).children!; - expect(spans, hasLength(5)); - - expect((spans[0] as TextSpan).text, 'a '); - - final link1 = spans[1] as TextSpan; - expect(link1.text, '123'); - (link1.recognizer! as TapGestureRecognizer).onTap!(); - verify(() => callback('123')).called(1); - - expect((spans[2] as TextSpan).text, ' b '); - - final link2 = spans[3] as TextSpan; - expect(link2.text, '456'); - (link2.recognizer! as TapGestureRecognizer).onTap!(); - verify(() => callback('456')).called(1); - - expect((spans[4] as TextSpan).text, ' c'); - }); - - test('Skip empty parts', () { - final spans = buildRichTextSpan( - text: '{actor}', - parameters: BuiltMap({ - 'actor': BuiltMap({ - 'type': JsonObject(core.RichObjectParameter_Type.user.name), - 'id': JsonObject(''), - 'name': JsonObject(''), - }), - }), - references: BuiltList(), - style: const TextStyle(), - onReferenceClicked: (_) {}, - ).children!; - expect(spans, hasLength(1)); - expect((spans[0] as WidgetSpan).child, isA()); - }); - }); -}