diff --git a/app/assets/javascripts/discourse/app/components/modal/associate-account-confirm.hbs b/app/assets/javascripts/discourse/app/components/modal/associate-account-confirm.hbs new file mode 100644 index 00000000000..fcd33674869 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/modal/associate-account-confirm.hbs @@ -0,0 +1,46 @@ +<DModal + @title={{i18n + "user.associated_accounts.confirm_modal_title" + provider=(i18n (concat "login." @model.provider_name ".name")) + }} + @closeModal={{@closeModal}} + @flash={{this.flash}} + @flashType="error" +> + <:body> + {{#if @model.existing_account_description}} + <p> + {{i18n + "user.associated_accounts.confirm_description.disconnect" + provider=(i18n (concat "login." @model.provider_name ".name")) + account_description=@model.existing_account_description + }} + </p> + {{/if}} + + <p> + {{#if @model.account_description}} + {{i18n + "user.associated_accounts.confirm_description.account_specific" + provider=(i18n (concat "login." @model.provider_name ".name")) + account_description=@model.account_description + }} + {{else}} + {{i18n + "user.associated_accounts.confirm_description.generic" + provider=(i18n (concat "login." @model.provider_name ".name")) + }} + {{/if}} + </p> + </:body> + + <:footer> + <DButton + @label="user.associated_accounts.connect" + @action={{this.finishConnect}} + @icon="plug" + class="btn-primary" + /> + <DButton @label="user.associated_accounts.cancel" @action={{@closeModal}} /> + </:footer> +</DModal> \ No newline at end of file diff --git a/app/assets/javascripts/discourse/app/components/modal/associate-account-confirm.js b/app/assets/javascripts/discourse/app/components/modal/associate-account-confirm.js new file mode 100644 index 00000000000..3857e20c2b1 --- /dev/null +++ b/app/assets/javascripts/discourse/app/components/modal/associate-account-confirm.js @@ -0,0 +1,35 @@ +import Component from "@glimmer/component"; +import { tracked } from "@glimmer/tracking"; +import { action } from "@ember/object"; +import { inject as service } from "@ember/service"; +import { ajax } from "discourse/lib/ajax"; +import { popupAjaxError } from "discourse/lib/ajax-error"; + +export default class AssociateAccountConfirm extends Component { + @service router; + @service currentUser; + + @tracked flash; + + @action + async finishConnect() { + try { + const result = await ajax({ + url: `/associate/${encodeURIComponent(this.args.model.token)}`, + type: "POST", + }); + + if (result.success) { + this.router.transitionTo( + "preferences.account", + this.currentUser.findDetails() + ); + this.args.closeModal(); + } else { + this.flash = result.error; + } + } catch (e) { + popupAjaxError(e); + } + } +} diff --git a/app/assets/javascripts/discourse/app/controllers/associate-account-confirm.js b/app/assets/javascripts/discourse/app/controllers/associate-account-confirm.js deleted file mode 100644 index c76f1f2dc27..00000000000 --- a/app/assets/javascripts/discourse/app/controllers/associate-account-confirm.js +++ /dev/null @@ -1,31 +0,0 @@ -import Controller from "@ember/controller"; -import { inject as service } from "@ember/service"; -import { ajax } from "discourse/lib/ajax"; -import { popupAjaxError } from "discourse/lib/ajax-error"; -import ModalFunctionality from "discourse/mixins/modal-functionality"; - -export default Controller.extend(ModalFunctionality, { - router: service(), - currentUser: service(), - - actions: { - finishConnect() { - ajax({ - url: `/associate/${encodeURIComponent(this.model.token)}`, - type: "POST", - }) - .then((result) => { - if (result.success) { - this.router.transitionTo( - "preferences.account", - this.currentUser.findDetails() - ); - this.send("closeModal"); - } else { - this.set("model.error", result.error); - } - }) - .catch(popupAjaxError); - }, - }, -}); diff --git a/app/assets/javascripts/discourse/app/routes/associate-account.js b/app/assets/javascripts/discourse/app/routes/associate-account.js index 36171170104..a27c2cfb1c9 100644 --- a/app/assets/javascripts/discourse/app/routes/associate-account.js +++ b/app/assets/javascripts/discourse/app/routes/associate-account.js @@ -1,15 +1,16 @@ import { action } from "@ember/object"; import { next } from "@ember/runloop"; import { inject as service } from "@ember/service"; +import AssociateAccountConfirm from "discourse/components/modal/associate-account-confirm"; import { ajax } from "discourse/lib/ajax"; import { popupAjaxError } from "discourse/lib/ajax-error"; import cookie from "discourse/lib/cookie"; -import showModal from "discourse/lib/show-modal"; import DiscourseRoute from "discourse/routes/discourse"; export default DiscourseRoute.extend({ router: service(), currentUser: service(), + modal: service(), beforeModel(transition) { if (!this.currentUser) { @@ -34,7 +35,7 @@ export default DiscourseRoute.extend({ const model = await ajax( `/associate/${encodeURIComponent(params.token)}.json` ); - showModal("associate-account-confirm", { model }); + this.modal.show(AssociateAccountConfirm, { model }); } catch (e) { popupAjaxError(e); } diff --git a/app/assets/javascripts/discourse/app/services/modal.js b/app/assets/javascripts/discourse/app/services/modal.js index 5bc879e3e3c..bfc5ea96d0e 100644 --- a/app/assets/javascripts/discourse/app/services/modal.js +++ b/app/assets/javascripts/discourse/app/services/modal.js @@ -14,7 +14,6 @@ import I18n from "discourse-i18n"; // Known legacy modals in core. Silence deprecation warnings for these so the messages // don't cause unnecessary noise. const KNOWN_LEGACY_MODALS = [ - "associate-account-confirm", "avatar-selector", "change-owner", "change-post-notice", diff --git a/app/assets/javascripts/discourse/app/templates/modal/associate-account-confirm.hbs b/app/assets/javascripts/discourse/app/templates/modal/associate-account-confirm.hbs deleted file mode 100644 index 84169f02da4..00000000000 --- a/app/assets/javascripts/discourse/app/templates/modal/associate-account-confirm.hbs +++ /dev/null @@ -1,50 +0,0 @@ -<DModalBody - @rawTitle={{i18n - "user.associated_accounts.confirm_modal_title" - provider=(i18n (concat "login." this.model.provider_name ".name")) - }} -> - {{#if this.model.error}} - <div class="alert alert-error"> - {{this.model.error}} - </div> - {{/if}} - - {{#if this.model.existing_account_description}} - <p> - {{i18n - "user.associated_accounts.confirm_description.disconnect" - provider=(i18n (concat "login." this.model.provider_name ".name")) - account_description=this.model.existing_account_description - }} - </p> - {{/if}} - - <p> - {{#if this.model.account_description}} - {{i18n - "user.associated_accounts.confirm_description.account_specific" - provider=(i18n (concat "login." this.model.provider_name ".name")) - account_description=this.model.account_description - }} - {{else}} - {{i18n - "user.associated_accounts.confirm_description.generic" - provider=(i18n (concat "login." this.model.provider_name ".name")) - }} - {{/if}} - </p> -</DModalBody> - -<div class="modal-footer"> - <DButton - @label="user.associated_accounts.connect" - @action={{action "finishConnect"}} - @icon="plug" - class="btn-primary" - /> - <DButton - @label="user.associated_accounts.cancel" - @action={{action "closeModal"}} - /> -</div> \ No newline at end of file diff --git a/app/assets/javascripts/discourse/tests/acceptance/invite-accept-test.js b/app/assets/javascripts/discourse/tests/acceptance/invite-accept-test.js index ccabacfdd12..fa928c3bb68 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/invite-accept-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/invite-accept-test.js @@ -1,6 +1,7 @@ -import { click, fillIn, visit } from "@ember/test-helpers"; +import { click, currentURL, fillIn, visit } from "@ember/test-helpers"; import { test } from "qunit"; import PreloadStore from "discourse/lib/preload-store"; +import pretender, { response } from "discourse/tests/helpers/create-pretender"; import { acceptance, exists, @@ -504,3 +505,76 @@ acceptance( }); } ); + +acceptance("Associate link", function (needs) { + needs.user(); + needs.settings({ enable_local_logins: false }); + + setAuthenticationData(needs.hooks, { + auth_provider: "facebook", + email: "blah@example.com", + email_valid: true, + username: "foobar", + name: "barfoo", + associate_url: "/associate/abcde", + }); + + test("associates the account", async function (assert) { + preloadInvite({ link: true }); + pretender.get("/associate/abcde.json", () => { + return response({ + token: "abcde", + provider_name: "facebook", + }); + }); + + pretender.post("/associate/abcde", () => { + return response({ success: true }); + }); + + await visit("/invites/my-valid-invite-token"); + assert + .dom(".create-account-associate-link") + .exists("shows the associate account link"); + + await click(".create-account-associate-link a"); + assert.dom(".d-modal").exists(); + + await click(".d-modal .btn-primary"); + assert.strictEqual(currentURL(), "/u/eviltrout/preferences/account"); + }); +}); + +acceptance("Associate link, with an error", function (needs) { + needs.user(); + needs.settings({ enable_local_logins: false }); + + setAuthenticationData(needs.hooks, { + auth_provider: "facebook", + email: "blah@example.com", + email_valid: true, + username: "foobar", + name: "barfoo", + associate_url: "/associate/abcde", + }); + + test("shows the error", async function (assert) { + preloadInvite({ link: true }); + pretender.get("/associate/abcde.json", () => { + return response({ + token: "abcde", + provider_name: "facebook", + }); + }); + + pretender.post("/associate/abcde", () => { + return response({ error: "sorry, no" }); + }); + + await visit("/invites/my-valid-invite-token"); + await click(".create-account-associate-link a"); + await click(".d-modal .btn-primary"); + + assert.dom(".d-modal .alert").hasText("sorry, no"); + }); +}); diff --git a/app/assets/javascripts/discourse/tests/acceptance/modal/login-test.js b/app/assets/javascripts/discourse/tests/acceptance/modal/login-test.js deleted file mode 100644 index c4ba17b2df5..00000000000 --- a/app/assets/javascripts/discourse/tests/acceptance/modal/login-test.js +++ /dev/null @@ -1,14 +0,0 @@ -import { click, tab, visit } from "@ember/test-helpers"; -import { test } from "qunit"; -import { acceptance } from "discourse/tests/helpers/qunit-helpers"; - -acceptance("Modal - Login", function () { - test("You can tab to the login button", async function (assert) { - await visit("/"); - await click("header .login-button"); - // you have to press the tab key twice to get to the login button - await tab({ unRestrainTabIndex: true }); - await tab({ unRestrainTabIndex: true }); - assert.dom(".modal-footer #login-button").isFocused(); - }); -});