summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Knox <psimyn@gmail.com>2019-06-11 14:15:08 +1000
committerSimon Knox <psimyn@gmail.com>2019-06-14 23:57:05 +1000
commit14f27102b696672f02b5d1b2ab45688b711f4024 (patch)
treeeef48bb1366b20a961e6aa4bb61bc357dc77d1c9
parent577832598f1b35187efafc426068ef7ac36ae09f (diff)
downloadgitlab-ce-62966-embed-zoom-call-in-issue-mvc.tar.gz
Add Join meeting button to Issues with Zoom links62966-embed-zoom-call-in-issue-mvc
Detect links containing zoom.us followed by j, s, or my Add link below Issue title that links to Zoom meeting
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue3
-rw-r--r--app/assets/javascripts/issue_show/components/pinned_links.vue52
-rw-r--r--app/assets/stylesheets/framework/common.scss1
-rw-r--r--changelogs/unreleased/62966-embed-zoom-call-in-issue-mvc.yml5
-rw-r--r--locale/gitlab.pot3
-rw-r--r--package.json2
-rw-r--r--spec/features/issues/user_creates_issue_spec.rb13
-rw-r--r--spec/frontend/issue_show/components/pinned_links_spec.js91
-rw-r--r--yarn.lock8
9 files changed, 173 insertions, 5 deletions
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index e88ca4747c5..de2a9664cde 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -11,6 +11,7 @@ import titleComponent from './title.vue';
import descriptionComponent from './description.vue';
import editedComponent from './edited.vue';
import formComponent from './form.vue';
+import PinnedLinks from './pinned_links.vue';
import recaptchaModalImplementor from '../../vue_shared/mixins/recaptcha_modal_implementor';
export default {
@@ -19,6 +20,7 @@ export default {
titleComponent,
editedComponent,
formComponent,
+ PinnedLinks,
},
mixins: [recaptchaModalImplementor],
props: {
@@ -340,6 +342,7 @@ export default {
:title-text="state.titleText"
:show-inline-edit-button="showInlineEditButton"
/>
+ <pinned-links :description-html="state.descriptionHtml" />
<description-component
v-if="state.descriptionHtml"
:can-update="canUpdate"
diff --git a/app/assets/javascripts/issue_show/components/pinned_links.vue b/app/assets/javascripts/issue_show/components/pinned_links.vue
new file mode 100644
index 00000000000..7a54b26bc2b
--- /dev/null
+++ b/app/assets/javascripts/issue_show/components/pinned_links.vue
@@ -0,0 +1,52 @@
+<script>
+import { GlLink } from '@gitlab/ui';
+import Icon from '~/vue_shared/components/icon.vue';
+
+export default {
+ components: {
+ Icon,
+ GlLink,
+ },
+ props: {
+ descriptionHtml: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ linksInDescription() {
+ const el = document.createElement('div');
+ el.innerHTML = this.descriptionHtml;
+ return [...el.querySelectorAll('a')].map(a => a.href);
+ },
+ // Detect links matching the following formats:
+ // Zoom Start links: https://zoom.us/s/<meeting-id>
+ // Zoom Join links: https://zoom.us/j/<meeting-id>
+ // Personal Zoom links: https://zoom.us/my/<meeting-id>
+ // Vanity Zoom links: https://gitlab.zoom.us/j/<meeting-id> (also /s and /my)
+ zoomHref() {
+ const zoomRegex = /^https:\/\/([\w\d-]+\.)?zoom\.us\/(s|j|my)\/.+/;
+ return this.linksInDescription.reduce((acc, currentLink) => {
+ let lastLink = acc;
+ if (zoomRegex.test(currentLink)) {
+ lastLink = currentLink;
+ }
+ return lastLink;
+ }, '');
+ },
+ },
+};
+</script>
+
+<template>
+ <div v-if="zoomHref" class="border-bottom mb-3 mt-n2">
+ <gl-link
+ :href="zoomHref"
+ target="_blank"
+ class="btn btn-inverted btn-secondary btn-sm text-dark mb-3"
+ >
+ <icon name="brand-zoom" :size="14" />
+ <strong class="vertical-align-top">{{ __('Join Zoom meeting') }}</strong>
+ </gl-link>
+ </div>
+</template>
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index db09118ba15..1bd5043ed10 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -416,6 +416,7 @@ img.emoji {
.center { text-align: center; }
.block { display: block; }
.flex { display: flex; }
+.vertical-align-top { vertical-align: top; }
.vertical-align-middle { vertical-align: middle; }
.vertical-align-sub { vertical-align: sub; }
.flex-align-self-center { align-self: center; }
diff --git a/changelogs/unreleased/62966-embed-zoom-call-in-issue-mvc.yml b/changelogs/unreleased/62966-embed-zoom-call-in-issue-mvc.yml
new file mode 100644
index 00000000000..a41873f671e
--- /dev/null
+++ b/changelogs/unreleased/62966-embed-zoom-call-in-issue-mvc.yml
@@ -0,0 +1,5 @@
+---
+title: Add Join meeting button to issues with Zoom links
+merge_request: 29454
+author:
+type: added
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 88ace6057cf..3791691a611 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5594,6 +5594,9 @@ msgstr ""
msgid "Job|with"
msgstr ""
+msgid "Join Zoom meeting"
+msgstr ""
+
msgid "Jul"
msgstr ""
diff --git a/package.json b/package.json
index 0758c27c75b..a981a9058ae 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,7 @@
"@babel/plugin-syntax-import-meta": "^7.2.0",
"@babel/preset-env": "^7.4.4",
"@gitlab/csslab": "^1.9.0",
- "@gitlab/svgs": "^1.63.0",
+ "@gitlab/svgs": "^1.64.0",
"@gitlab/ui": "^3.11.0",
"apollo-cache-inmemory": "^1.5.1",
"apollo-client": "^2.5.1",
diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb
index 0f604db870f..2789d574156 100644
--- a/spec/features/issues/user_creates_issue_spec.rb
+++ b/spec/features/issues/user_creates_issue_spec.rb
@@ -92,6 +92,19 @@ describe "User creates issue" do
.and have_content(label_titles.first)
end
end
+
+ context "with Zoom link" do
+ it "adds Zoom button" do
+ issue_title = "Issue containing Zoom meeting link"
+ zoom_url = "https://gitlab.zoom.us/j/123456789"
+
+ fill_in("Title", with: issue_title)
+ fill_in("Description", with: zoom_url)
+ click_button("Submit issue")
+
+ expect(page).to have_link('Join Zoom meeting', href: zoom_url)
+ end
+ end
end
context "when signed in as user with special characters in their name" do
diff --git a/spec/frontend/issue_show/components/pinned_links_spec.js b/spec/frontend/issue_show/components/pinned_links_spec.js
new file mode 100644
index 00000000000..50041667a61
--- /dev/null
+++ b/spec/frontend/issue_show/components/pinned_links_spec.js
@@ -0,0 +1,91 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { GlLink } from '@gitlab/ui';
+import PinnedLinks from '~/issue_show/components/pinned_links.vue';
+
+const localVue = createLocalVue();
+
+const plainZoomUrl = 'https://zoom.us/j/123456789';
+const vanityZoomUrl = 'https://gitlab.zoom.us/j/123456789';
+const startZoomUrl = 'https://zoom.us/s/123456789';
+const personalZoomUrl = 'https://zoom.us/my/hunter-zoloman';
+const randomUrl = 'https://zoom.us.com';
+
+describe('PinnedLinks', () => {
+ let wrapper;
+
+ const link = {
+ get text() {
+ return wrapper.find(GlLink).text();
+ },
+ get href() {
+ return wrapper.find(GlLink).attributes('href');
+ },
+ };
+
+ const createComponent = props => {
+ wrapper = shallowMount(localVue.extend(PinnedLinks), {
+ localVue,
+ sync: false,
+ propsData: {
+ descriptionHtml: '',
+ ...props,
+ },
+ });
+ };
+
+ it('displays Zoom link', () => {
+ createComponent({
+ descriptionHtml: `<a href="${plainZoomUrl}">Zoom</a>`,
+ });
+
+ expect(link.text).toBe('Join Zoom meeting');
+ });
+
+ it('detects plain Zoom link', () => {
+ createComponent({
+ descriptionHtml: `<a href="${plainZoomUrl}">Zoom</a>`,
+ });
+
+ expect(link.href).toBe(plainZoomUrl);
+ });
+
+ it('detects vanity Zoom link', () => {
+ createComponent({
+ descriptionHtml: `<a href="${vanityZoomUrl}">Zoom</a>`,
+ });
+
+ expect(link.href).toBe(vanityZoomUrl);
+ });
+
+ it('detects Zoom start meeting link', () => {
+ createComponent({
+ descriptionHtml: `<a href="${startZoomUrl}">Zoom</a>`,
+ });
+
+ expect(link.href).toBe(startZoomUrl);
+ });
+
+ it('detects personal Zoom room link', () => {
+ createComponent({
+ descriptionHtml: `<a href="${personalZoomUrl}">Zoom</a>`,
+ });
+
+ expect(link.href).toBe(personalZoomUrl);
+ });
+
+ it('only renders final Zoom link in description', () => {
+ createComponent({
+ descriptionHtml: `<a href="${plainZoomUrl}">Zoom</a><a href="${vanityZoomUrl}">Zoom</a>`,
+ });
+
+ expect(link.href).toBe(vanityZoomUrl);
+ });
+
+ it('does not render for other links', () => {
+ createComponent({
+ descriptionHtml: `<a href="${randomUrl}">Some other link</a>`,
+ });
+
+ expect(wrapper.find(GlLink).exists()).toBe(false);
+ });
+});
diff --git a/yarn.lock b/yarn.lock
index dddf01414b2..f97e7363331 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -700,10 +700,10 @@
dependencies:
requireindex "~1.1.0"
-"@gitlab/svgs@^1.63.0":
- version "1.63.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.63.0.tgz#9dd544026d203e4ce6efed72b05db68f710c4d49"
- integrity sha512-YztrReFTg31B7v5wtUC5j15KHNcMebtW+kACytEU42XomMaIwk4USIbygqWlq0VRHA2VHJrHApfJHIjxiCCQcA==
+"@gitlab/svgs@^1.64.0":
+ version "1.64.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.64.0.tgz#1370bcbe9ca0ecc9fb919956cd4241bea090ddd3"
+ integrity sha512-y9p73NGDnQJc18Dtk0oJfgxedancBT6UceATcnZMceLV6iWylzdMbQWxCl4O2aBXwsAoCrLUJQ9jhRkbNicYNA==
"@gitlab/ui@^3.11.0":
version "3.11.0"