From 48935e2168c014ce4968fde37cf5850e1216c927 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 26 Aug 2022 16:08:58 +0200 Subject: [PATCH] Add global search Signed-off-by: Thomas Citharel --- config/config.exs | 8 + js/.eslintrc.js | 5 +- js/.gitignore | 3 + js/get_union_json.ts | 4 +- js/package.json | 2 + js/playwright.config.ts | 107 ++++ js/{src/assets => public/img}/logo.svg | 0 js/src/App.vue | 58 +- js/src/apollo/error-link.ts | 2 +- js/src/apollo/link.ts | 39 +- js/src/apollo/user.ts | 8 +- js/src/apollo/utils.ts | 7 +- js/src/assets/oruga-tailwindcss.css | 78 ++- js/src/assets/tailwind.css | 6 +- js/src/components/Account/ActorCard.vue | 2 +- .../Activity/DiscussionActivityItem.vue | 10 +- .../components/Activity/EventActivityItem.vue | 10 +- .../components/Activity/GroupActivityItem.vue | 20 +- .../Activity/MemberActivityItem.vue | 10 +- .../components/Activity/PostActivityItem.vue | 10 +- .../Activity/ResourceActivityItem.vue | 10 +- js/src/components/Activity/activity.scss | 6 +- js/src/components/Address/AddressInfo.vue | 2 +- js/src/components/Categories/constants.ts | 101 ---- js/src/components/Comment/CommentTree.vue | 8 +- ...mment.story.vue => EventComment.story.vue} | 18 +- .../Comment/{Comment.vue => EventComment.vue} | 8 +- .../Discussion/DiscussionComment.vue | 8 +- js/src/components/ErrorComponent.vue | 2 +- js/src/components/Event/EventCard.story.vue | 11 +- js/src/components/Event/EventCard.vue | 122 +++- js/src/components/Event/EventFullDate.vue | 41 +- js/src/components/Event/EventMap.vue | 2 +- js/src/components/Event/EventMetadataList.vue | 4 +- .../components/Event/EventMetadataSidebar.vue | 2 +- .../components/Event/EventMinimalistCard.vue | 21 +- .../Event/EventParticipationCard.vue | 15 +- .../Event/FullAddressAutoComplete.vue | 27 +- .../Event/GroupedMultiEventMinimalistCard.vue | 2 +- .../{Etherpad.vue => EtherpadIntegration.vue} | 0 ...JitsiMeet.vue => JitsiMeetIntegration.vue} | 0 .../{PeerTube.vue => PeerTubeIntegration.vue} | 4 - .../{Twitch.vue => TwitchIntegration.vue} | 0 .../{YouTube.vue => YouTubeIntegration.vue} | 4 - js/src/components/Event/OrganizerPicker.vue | 4 +- .../Event/OrganizerPickerWrapper.vue | 8 +- .../Event/ParticipationButton.story.vue | 2 +- js/src/components/Event/ShareEventModal.vue | 2 +- js/src/components/Event/TagInput.story.vue | 4 +- js/src/components/Event/TagInput.vue | 8 +- js/src/components/Group/GroupCard.story.vue | 16 +- js/src/components/Group/GroupCard.vue | 99 +++- .../Group/GroupMemberCard.story.vue | 28 +- js/src/components/Group/GroupMemberCard.vue | 11 +- js/src/components/Group/InvitationCard.vue | 33 +- ...Discussions.vue => DiscussionsSection.vue} | 0 .../{Events.vue => EventsSection.vue} | 0 .../Sections/{Posts.vue => PostsSection.vue} | 0 .../{Resources.vue => ResourcesSection.vue} | 0 .../components/Group/SkeletonGroupResult.vue | 18 + js/src/components/Home/CategoriesPreview.vue | 11 +- js/src/components/Home/SearchFields.vue | 45 +- .../components/Home/UnloggedIntroduction.vue | 9 +- js/src/components/{Map.vue => LeafletMap.vue} | 0 js/src/components/Local/CloseContent.vue | 2 +- js/src/components/Local/CloseEvents.vue | 32 +- js/src/components/Local/CloseGroups.vue | 11 +- js/src/components/Local/LastEvents.vue | 3 +- js/src/components/Local/OnlineEvents.vue | 35 +- js/src/components/MobilizonLogo.vue | 2 +- js/src/components/NavBar.vue | 102 +++- .../components/{Footer.vue => PageFooter.vue} | 0 .../Participation/ConfirmParticipation.vue | 26 +- .../Participation/ParticipationSection.vue | 10 +- .../Participation/UnloggedParticipation.vue | 4 +- js/src/components/PictureUpload.vue | 2 +- js/src/components/Report/ReportCard.vue | 2 +- js/src/components/Resource/FolderItem.vue | 8 +- js/src/components/Resource/ResourceItem.vue | 4 +- .../components/Resource/ResourceSelector.vue | 2 +- .../Settings/NotificationsOnboarding.vue | 4 +- .../components/Settings/SettingMenuItem.vue | 13 +- .../Settings/SettingMenuSection.vue | 4 +- js/src/components/Settings/SettingsMenu.vue | 2 +- js/src/components/Share/DiasporaLogo.vue | 4 +- js/src/components/Share/MastodonLogo.vue | 2 +- js/src/components/Share/ShareModal.vue | 22 +- js/src/components/Share/TelegramLogo.vue | 2 +- js/src/components/Tag.vue | 21 +- .../components/{Editor.vue => TextEditor.vue} | 87 +-- js/src/components/Todo/CompactTodo.vue | 4 +- js/src/components/Todo/FullTodo.vue | 2 +- .../{Breadcrumbs.vue => NavBreadcrumbs.vue} | 5 +- .../components/Utils/RedirectWithAccount.vue | 8 +- js/src/components/core/Button.vue | 74 --- .../core/{Dialog.vue => CustomDialog.vue} | 4 +- .../core/{Snackbar.vue => CustomSnackbar.vue} | 0 js/src/components/core/Field.vue | 33 -- js/src/components/core/Input.vue | 292 ---------- js/src/components/core/LinkOrRouterLink.vue | 39 ++ js/src/components/core/MaterialIcon.vue | 2 + js/src/components/core/Message.vue | 57 -- js/src/components/core/Switch.vue | 27 - js/src/composition/apollo/actor.ts | 2 +- js/src/composition/apollo/config.ts | 10 + js/src/composition/apollo/group.ts | 46 +- js/src/filters/datetime.ts | 9 +- js/src/graphql/config.ts | 20 + js/src/graphql/report.ts | 7 + js/src/graphql/search.ts | 57 +- js/src/i18n/en_US.json | 9 +- js/src/i18n/fr_FR.json | 9 +- js/src/main.ts | 43 +- js/src/oruga-config.ts | 9 + js/src/plugins/dialog.ts | 2 +- js/src/plugins/notifier.ts | 8 +- js/src/plugins/snackbar.ts | 4 +- js/src/router/actor.ts | 4 +- js/src/router/discussion.ts | 28 +- js/src/router/event.ts | 8 +- js/src/router/groups.ts | 12 +- js/src/router/index.ts | 13 +- js/src/router/settings.ts | 19 +- js/src/router/user.ts | 2 +- js/src/service-worker.ts | 4 +- js/src/services/push-subscription.ts | 12 +- js/src/services/statistics/index.ts | 4 +- js/src/services/statistics/sentry.ts | 2 - js/src/types/actor/group.model.ts | 2 + js/src/types/config.model.ts | 6 + js/src/types/enums.ts | 5 + js/src/types/event.model.ts | 2 + js/src/types/post.model.ts | 2 +- js/src/utils/auth.ts | 4 +- js/src/utils/datetime.ts | 23 +- js/src/views/About.vue | 11 +- ...boutInstance.vue => AboutInstanceView.vue} | 0 .../About/{Glossary.vue => GlossaryView.vue} | 0 .../About/{Privacy.vue => PrivacyView.vue} | 0 .../views/About/{Rules.vue => RulesView.vue} | 0 .../views/About/{Terms.vue => TermsView.vue} | 0 .../{Register.vue => RegisterView.vue} | 0 .../views/Account/children/EditIdentity.vue | 6 +- js/src/views/Admin/AdminGroupProfile.vue | 98 ++-- js/src/views/Admin/AdminProfile.vue | 53 +- js/src/views/Admin/AdminUserProfile.vue | 20 +- js/src/views/Admin/GroupProfiles.vue | 32 +- .../Admin/{Instance.vue => InstanceView.vue} | 25 +- .../{Instances.vue => InstancesView.vue} | 66 ++- .../Admin/{Profiles.vue => ProfilesView.vue} | 40 +- .../Admin/{Settings.vue => SettingsView.vue} | 0 .../views/Admin/{Users.vue => UsersView.vue} | 0 js/src/views/CategoriesView.vue | 13 +- .../{Create.vue => CreateView.vue} | 4 +- .../{Discussion.vue => DiscussionView.vue} | 7 +- ...ssionsList.vue => DiscussionsListView.vue} | 1 + js/src/views/Event/{Edit.vue => EditView.vue} | 229 ++++---- .../views/Event/{Event.vue => EventView.vue} | 110 ++-- js/src/views/Event/GroupEvents.vue | 2 +- .../Event/{MyEvents.vue => MyEventsView.vue} | 9 +- ...{Participants.vue => ParticipantsView.vue} | 129 ++--- .../Group/{Create.vue => CreateView.vue} | 91 +-- js/src/views/Group/GroupMembers.vue | 81 +-- js/src/views/Group/GroupSettings.vue | 157 ++--- .../views/Group/{Group.vue => GroupView.vue} | 85 +-- .../Group/{Settings.vue => SettingsView.vue} | 2 +- .../Group/{Timeline.vue => TimelineView.vue} | 65 +-- js/src/views/HomeView.vue | 67 +-- .../views/{Interact.vue => InteractView.vue} | 9 +- .../Moderation/{Logs.vue => LogsView.vue} | 0 js/src/views/Moderation/Report.vue | 526 ----------------- .../{ReportList.vue => ReportListView.vue} | 34 +- js/src/views/Moderation/ReportView.vue | 543 ++++++++++++++++++ js/src/views/Posts/{Edit.vue => EditView.vue} | 48 +- js/src/views/Posts/{List.vue => ListView.vue} | 0 js/src/views/Posts/{Post.vue => PostView.vue} | 29 +- js/src/views/Resources/ResourceFolder.vue | 101 ++-- js/src/views/SearchView.vue | 312 ++++++---- js/src/views/Settings/NotificationsView.vue | 6 +- js/src/views/Settings/PreferencesView.vue | 2 +- js/src/views/Todos/TodoLists.vue | 2 +- js/src/views/User/LoginView.vue | 13 +- js/src/views/User/RegisterView.vue | 49 +- js/src/views/User/SettingsOnboard.vue | 2 +- js/tailwind.config.js | 55 +- js/tests/e2e/login.spec.ts | 73 +++ .../__snapshots__/CommentTree.spec.ts.snap | 60 +- .../ParticipationSection.spec.ts | 4 +- .../__snapshots__/PostListItem.spec.ts.snap | 28 +- .../__snapshots__/ReportModal.spec.ts.snap | 18 +- .../__snapshots__/navbar.spec.ts.snap | 26 +- js/tests/unit/specs/mocks/matchMedia.ts | 16 +- js/vite.config.js | 1 + js/yarn.lock | 33 ++ lib/graphql/api/search.ex | 64 ++- lib/graphql/resolvers/address.ex | 3 +- lib/graphql/resolvers/config.ex | 12 +- lib/graphql/resolvers/followers.ex | 4 + lib/graphql/resolvers/member.ex | 4 + lib/graphql/schema/actor.ex | 4 +- lib/graphql/schema/actors/application.ex | 4 +- lib/graphql/schema/actors/group.ex | 15 +- lib/graphql/schema/actors/person.ex | 13 +- lib/graphql/schema/config.ex | 11 + lib/graphql/schema/event.ex | 2 +- lib/graphql/schema/search.ex | 121 +++- lib/mobilizon/actors/actors.ex | 4 +- lib/mobilizon/events/events.ex | 15 +- lib/service/global_search/event_result.ex | 18 + lib/service/global_search/global_search.ex | 17 + lib/service/global_search/group_result.ex | 19 + lib/service/global_search/provider.ex | 40 ++ lib/service/global_search/search_mobilizon.ex | 225 ++++++++ lib/service/pictures/information.ex | 3 + lib/service/pictures/unsplash.ex | 10 +- lib/web/templates/page/index.html.heex | 6 +- 216 files changed, 3646 insertions(+), 2806 deletions(-) create mode 100644 js/playwright.config.ts rename js/{src/assets => public/img}/logo.svg (100%) rename js/src/components/Comment/{Comment.story.vue => EventComment.story.vue} (92%) rename js/src/components/Comment/{Comment.vue => EventComment.vue} (98%) rename js/src/components/Event/Integrations/{Etherpad.vue => EtherpadIntegration.vue} (100%) rename js/src/components/Event/Integrations/{JitsiMeet.vue => JitsiMeetIntegration.vue} (100%) rename js/src/components/Event/Integrations/{PeerTube.vue => PeerTubeIntegration.vue} (93%) rename js/src/components/Event/Integrations/{Twitch.vue => TwitchIntegration.vue} (100%) rename js/src/components/Event/Integrations/{YouTube.vue => YouTubeIntegration.vue} (93%) rename js/src/components/Group/Sections/{Discussions.vue => DiscussionsSection.vue} (100%) rename js/src/components/Group/Sections/{Events.vue => EventsSection.vue} (100%) rename js/src/components/Group/Sections/{Posts.vue => PostsSection.vue} (100%) rename js/src/components/Group/Sections/{Resources.vue => ResourcesSection.vue} (100%) create mode 100644 js/src/components/Group/SkeletonGroupResult.vue rename js/src/components/{Map.vue => LeafletMap.vue} (100%) rename js/src/components/{Footer.vue => PageFooter.vue} (100%) rename js/src/components/{Editor.vue => TextEditor.vue} (84%) rename js/src/components/Utils/{Breadcrumbs.vue => NavBreadcrumbs.vue} (94%) delete mode 100644 js/src/components/core/Button.vue rename js/src/components/core/{Dialog.vue => CustomDialog.vue} (97%) rename js/src/components/core/{Snackbar.vue => CustomSnackbar.vue} (100%) delete mode 100644 js/src/components/core/Field.vue delete mode 100644 js/src/components/core/Input.vue create mode 100644 js/src/components/core/LinkOrRouterLink.vue delete mode 100644 js/src/components/core/Message.vue delete mode 100644 js/src/components/core/Switch.vue rename js/src/views/About/{AboutInstance.vue => AboutInstanceView.vue} (100%) rename js/src/views/About/{Glossary.vue => GlossaryView.vue} (100%) rename js/src/views/About/{Privacy.vue => PrivacyView.vue} (100%) rename js/src/views/About/{Rules.vue => RulesView.vue} (100%) rename js/src/views/About/{Terms.vue => TermsView.vue} (100%) rename js/src/views/Account/{Register.vue => RegisterView.vue} (100%) rename js/src/views/Admin/{Instance.vue => InstanceView.vue} (90%) rename js/src/views/Admin/{Instances.vue => InstancesView.vue} (80%) rename js/src/views/Admin/{Profiles.vue => ProfilesView.vue} (82%) rename js/src/views/Admin/{Settings.vue => SettingsView.vue} (100%) rename js/src/views/Admin/{Users.vue => UsersView.vue} (100%) rename js/src/views/Discussions/{Create.vue => CreateView.vue} (97%) rename js/src/views/Discussions/{Discussion.vue => DiscussionView.vue} (98%) rename js/src/views/Discussions/{DiscussionsList.vue => DiscussionsListView.vue} (98%) rename js/src/views/Event/{Edit.vue => EditView.vue} (86%) rename js/src/views/Event/{Event.vue => EventView.vue} (94%) rename js/src/views/Event/{MyEvents.vue => MyEventsView.vue} (98%) rename js/src/views/Event/{Participants.vue => ParticipantsView.vue} (84%) rename js/src/views/Group/{Create.vue => CreateView.vue} (81%) rename js/src/views/Group/{Group.vue => GroupView.vue} (95%) rename js/src/views/Group/{Settings.vue => SettingsView.vue} (95%) rename js/src/views/Group/{Timeline.vue => TimelineView.vue} (86%) rename js/src/views/{Interact.vue => InteractView.vue} (93%) rename js/src/views/Moderation/{Logs.vue => LogsView.vue} (100%) delete mode 100644 js/src/views/Moderation/Report.vue rename js/src/views/Moderation/{ReportList.vue => ReportListView.vue} (83%) create mode 100644 js/src/views/Moderation/ReportView.vue rename js/src/views/Posts/{Edit.vue => EditView.vue} (92%) rename js/src/views/Posts/{List.vue => ListView.vue} (100%) rename js/src/views/Posts/{Post.vue => PostView.vue} (95%) create mode 100644 js/tests/e2e/login.spec.ts create mode 100644 lib/service/global_search/event_result.ex create mode 100644 lib/service/global_search/global_search.ex create mode 100644 lib/service/global_search/group_result.ex create mode 100644 lib/service/global_search/provider.ex create mode 100644 lib/service/global_search/search_mobilizon.ex diff --git a/config/config.exs b/config/config.exs index 7ef1b213c..3651b9d6a 100644 --- a/config/config.exs +++ b/config/config.exs @@ -365,6 +365,14 @@ config :mobilizon, Mobilizon.Service.Pictures.Unsplash, app_name: "Mobilizon", access_key: nil +config :mobilizon, :search, global: [is_default_search: false, is_enabled: true] + +config :mobilizon, Mobilizon.Service.GlobalSearch, + service: Mobilizon.Service.GlobalSearch.SearchMobilizon + +config :mobilizon, Mobilizon.Service.GlobalSearch.SearchMobilizon, + endpoint: "https://search.joinmobilizon.org" + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{config_env()}.exs" diff --git a/js/.eslintrc.js b/js/.eslintrc.js index 96907e6d5..c2b3c63fa 100644 --- a/js/.eslintrc.js +++ b/js/.eslintrc.js @@ -11,7 +11,7 @@ module.exports = { extends: [ "eslint:recommended", "plugin:vue/vue3-essential", - "@vue/eslint-config-typescript", + "@vue/eslint-config-typescript/recommended", "plugin:prettier/recommended", "@vue/eslint-config-prettier", ], @@ -24,12 +24,11 @@ module.exports = { }, rules: { - "no-console": process.env.NODE_ENV === "production" ? "warn" : "off", "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", "no-underscore-dangle": [ "error", { - allow: ["__typename"], + allow: ["__typename", "__schema"], }, ], "@typescript-eslint/no-explicit-any": "off", diff --git a/js/.gitignore b/js/.gitignore index e8b645d0b..5c5176c48 100644 --- a/js/.gitignore +++ b/js/.gitignore @@ -24,3 +24,6 @@ yarn-error.log* *.njsproj *.sln *.sw? +/test-results/ +/playwright-report/ +/playwright/.cache/ diff --git a/js/get_union_json.ts b/js/get_union_json.ts index 287cc9982..9a36881d1 100644 --- a/js/get_union_json.ts +++ b/js/get_union_json.ts @@ -1,5 +1,5 @@ -const fetch = require("node-fetch"); -const fs = require("fs"); +import fetch from "node-fetch"; +import fs from "fs"; fetch(`http://localhost:4000/api`, { method: "POST", diff --git a/js/package.json b/js/package.json index 60a992e05..94dc34c15 100644 --- a/js/package.json +++ b/js/package.json @@ -51,6 +51,7 @@ "@vue-leaflet/vue-leaflet": "^0.6.1", "@vue/apollo-composable": "^4.0.0-alpha.17", "@vue/compiler-sfc": "^3.2.37", + "@vueuse/core": "^9.1.0", "@vueuse/head": "^0.7.9", "@vueuse/router": "^9.0.2", "@xiaoshuapp/draggable": "^4.1.0", @@ -93,6 +94,7 @@ "devDependencies": { "@histoire/plugin-vue": "^0.10.0", "@intlify/vite-plugin-vue-i18n": "^6.0.0", + "@playwright/test": "^1.25.1", "@rushstack/eslint-patch": "^1.1.4", "@tailwindcss/forms": "^0.5.2", "@tailwindcss/typography": "^0.5.4", diff --git a/js/playwright.config.ts b/js/playwright.config.ts new file mode 100644 index 000000000..c3da6c26e --- /dev/null +++ b/js/playwright.config.ts @@ -0,0 +1,107 @@ +import type { PlaywrightTestConfig } from "@playwright/test"; +import { devices } from "@playwright/test"; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +const config: PlaywrightTestConfig = { + testDir: "./tests/e2e", + /* Maximum time one test can run for. */ + timeout: 30 * 1000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 5000, + }, + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: "html", + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 0, + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: "http://localhost:4005", + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: "on-first-retry", + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: "chromium", + use: { + ...devices["Desktop Chrome"], + }, + }, + + { + name: "firefox", + use: { + ...devices["Desktop Firefox"], + }, + }, + + // { + // name: 'webkit', + // use: { + // ...devices['Desktop Safari'], + // }, + // }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { + // ...devices['Pixel 5'], + // }, + // }, + // { + // name: 'Mobile Safari', + // use: { + // ...devices['iPhone 12'], + // }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { + // channel: 'msedge', + // }, + // }, + // { + // name: 'Google Chrome', + // use: { + // channel: 'chrome', + // }, + // }, + ], + + /* Folder for test artifacts such as screenshots, videos, traces, etc. */ + // outputDir: 'test-results/', + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // port: 3000, + // }, +}; + +export default config; diff --git a/js/src/assets/logo.svg b/js/public/img/logo.svg similarity index 100% rename from js/src/assets/logo.svg rename to js/public/img/logo.svg diff --git a/js/src/App.vue b/js/src/App.vue index f6d844c87..175d22d41 100644 --- a/js/src/App.vue +++ b/js/src/App.vue @@ -32,17 +32,17 @@ diff --git a/js/src/components/Settings/SettingMenuSection.vue b/js/src/components/Settings/SettingMenuSection.vue index 091f45be5..173839567 100644 --- a/js/src/components/Settings/SettingMenuSection.vue +++ b/js/src/components/Settings/SettingMenuSection.vue @@ -1,5 +1,7 @@