diff options
Diffstat (limited to 'app/assets/javascripts/snippets')
10 files changed, 326 insertions, 18 deletions
diff --git a/app/assets/javascripts/snippets/components/app.vue b/app/assets/javascripts/snippets/components/app.vue index e3d6cdd4606..bd2cb8e4595 100644 --- a/app/assets/javascripts/snippets/components/app.vue +++ b/app/assets/javascripts/snippets/components/app.vue @@ -1,10 +1,16 @@ <script> -import getSnippet from '../queries/getSnippet.query.graphql'; +import GetSnippetQuery from '../queries/snippet.query.graphql'; +import SnippetHeader from './snippet_header.vue'; +import { GlLoadingIcon } from '@gitlab/ui'; export default { + components: { + SnippetHeader, + GlLoadingIcon, + }, apollo: { - snippetData: { - query: getSnippet, + snippet: { + query: GetSnippetQuery, variables() { return { ids: this.snippetGid, @@ -21,11 +27,24 @@ export default { }, data() { return { - snippetData: {}, + snippet: {}, }; }, + computed: { + isLoading() { + return this.$apollo.queries.snippet.loading; + }, + }, }; </script> <template> - <div class="js-snippet-view"></div> + <div class="js-snippet-view"> + <gl-loading-icon + v-if="isLoading" + :label="__('Loading snippet')" + :size="2" + class="loading-animation prepend-top-20 append-bottom-20" + /> + <snippet-header v-else :snippet="snippet" /> + </div> </template> diff --git a/app/assets/javascripts/snippets/components/snippet_header.vue b/app/assets/javascripts/snippets/components/snippet_header.vue new file mode 100644 index 00000000000..e8f1bfeaf43 --- /dev/null +++ b/app/assets/javascripts/snippets/components/snippet_header.vue @@ -0,0 +1,241 @@ +<script> +import { __ } from '~/locale'; +import { + GlAvatar, + GlIcon, + GlSprintf, + GlButton, + GlModal, + GlAlert, + GlLoadingIcon, + GlDropdown, + GlDropdownItem, +} from '@gitlab/ui'; +import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; + +import DeleteSnippetMutation from '../mutations/deleteSnippet.mutation.graphql'; +import CanCreatePersonalSnippet from '../queries/userPermissions.query.graphql'; +import CanCreateProjectSnippet from '../queries/projectPermissions.query.graphql'; + +export default { + components: { + GlAvatar, + GlIcon, + GlSprintf, + GlButton, + GlModal, + GlAlert, + GlLoadingIcon, + GlDropdown, + GlDropdownItem, + TimeAgoTooltip, + }, + apollo: { + canCreateSnippet: { + query() { + return this.snippet.project ? CanCreateProjectSnippet : CanCreatePersonalSnippet; + }, + variables() { + return { + fullPath: this.snippet.project ? this.snippet.project.fullPath : undefined, + }; + }, + update(data) { + return this.snippet.project + ? data.project.userPermissions.createSnippet + : data.currentUser.userPermissions.createSnippet; + }, + }, + }, + props: { + snippet: { + type: Object, + required: true, + }, + }, + data() { + return { + isDeleting: false, + errorMessage: '', + canCreateSnippet: false, + }; + }, + computed: { + personalSnippetActions() { + return [ + { + condition: this.snippet.userPermissions.updateSnippet, + text: __('Edit'), + href: this.editLink, + click: undefined, + variant: 'outline-info', + cssClass: undefined, + }, + { + condition: this.snippet.userPermissions.adminSnippet, + text: __('Delete'), + href: undefined, + click: this.showDeleteModal, + variant: 'outline-danger', + cssClass: 'btn-inverted btn-danger ml-2', + }, + { + condition: this.canCreateSnippet, + text: __('New snippet'), + href: this.snippet.project + ? `${this.snippet.project.webUrl}/snippets/new` + : '/snippets/new', + click: undefined, + variant: 'outline-success', + cssClass: 'btn-inverted btn-success ml-2', + }, + ]; + }, + editLink() { + return `${this.snippet.webUrl}/edit`; + }, + visibility() { + return this.snippet.visibilityLevel; + }, + snippetVisibilityLevelDescription() { + switch (this.visibility) { + case 'private': + return this.snippet.project !== null + ? __('The snippet is visible only to project members.') + : __('The snippet is visible only to me.'); + case 'internal': + return __('The snippet is visible to any logged in user.'); + default: + return __('The snippet can be accessed without any authentication.'); + } + }, + visibilityLevelIcon() { + switch (this.visibility) { + case 'private': + return 'lock'; + case 'internal': + return 'shield'; + default: + return 'earth'; + } + }, + }, + methods: { + redirectToSnippets() { + window.location.pathname = 'dashboard/snippets'; + }, + closeDeleteModal() { + this.$refs.deleteModal.hide(); + }, + showDeleteModal() { + this.$refs.deleteModal.show(); + }, + deleteSnippet() { + this.isDeleting = true; + this.$apollo + .mutate({ + mutation: DeleteSnippetMutation, + variables: { id: this.snippet.id }, + }) + .then(() => { + this.isDeleting = false; + this.errorMessage = undefined; + this.closeDeleteModal(); + this.redirectToSnippets(); + }) + .catch(err => { + this.isDeleting = false; + this.errorMessage = err.message; + }); + }, + }, +}; +</script> +<template> + <div class="detail-page-header"> + <div class="detail-page-header-body"> + <div + class="snippet-box qa-snippet-box has-tooltip d-flex align-items-center append-right-5 mb-1" + :title="snippetVisibilityLevelDescription" + data-container="body" + > + <span class="sr-only"> + {{ s__(`VisibilityLevel|${visibility}`) }} + </span> + <gl-icon :name="visibilityLevelIcon" :size="14" /> + </div> + <div class="creator"> + <gl-sprintf message="Authored %{timeago} by %{author}"> + <template #timeago> + <time-ago-tooltip + :time="snippet.createdAt" + tooltip-placement="bottom" + css-class="snippet_updated_ago" + /> + </template> + <template #author> + <a :href="snippet.author.webUrl" class="d-inline"> + <gl-avatar :size="24" :src="snippet.author.avatarUrl" /> + <span class="bold">{{ snippet.author.name }}</span> + </a> + </template> + </gl-sprintf> + </div> + </div> + + <div class="detail-page-header-actions"> + <div class="d-none d-sm-block"> + <template v-for="(action, index) in personalSnippetActions"> + <gl-button + v-if="action.condition" + :key="index" + :variant="action.variant" + :class="action.cssClass" + :href="action.href || undefined" + @click="action.click ? action.click() : undefined" + > + {{ action.text }} + </gl-button> + </template> + </div> + <div class="d-block d-sm-none dropdown"> + <gl-dropdown :text="__('Options')" class="w-100" toggle-class="text-center"> + <gl-dropdown-item + v-for="(action, index) in personalSnippetActions" + :key="index" + :href="action.href || undefined" + @click="action.click ? action.click() : undefined" + >{{ action.text }}</gl-dropdown-item + > + </gl-dropdown> + </div> + </div> + + <gl-modal ref="deleteModal" modal-id="delete-modal" title="Example title"> + <template #modal-title>{{ __('Delete snippet?') }}</template> + + <gl-alert v-if="errorMessage" variant="danger" class="mb-2" @dismiss="errorMessage = ''">{{ + errorMessage + }}</gl-alert> + + <gl-sprintf message="Are you sure you want to delete %{name}?"> + <template #name + ><strong>{{ snippet.title }}</strong></template + > + </gl-sprintf> + + <template #modal-footer> + <gl-button @click="closeDeleteModal">{{ __('Cancel') }}</gl-button> + <gl-button + variant="danger" + :disabled="isDeleting" + data-qa-selector="delete_snippet_button" + @click="deleteSnippet" + > + <gl-loading-icon v-if="isDeleting" inline /> + {{ __('Delete snippet') }} + </gl-button> + </template> + </gl-modal> + </div> +</template> diff --git a/app/assets/javascripts/snippets/fragments/author.fragment.graphql b/app/assets/javascripts/snippets/fragments/author.fragment.graphql new file mode 100644 index 00000000000..2684bd0fa37 --- /dev/null +++ b/app/assets/javascripts/snippets/fragments/author.fragment.graphql @@ -0,0 +1,8 @@ +fragment Author on Snippet { + author { + name, + avatarUrl, + username, + webUrl + } +}
\ No newline at end of file diff --git a/app/assets/javascripts/snippets/fragments/project.fragment.graphql b/app/assets/javascripts/snippets/fragments/project.fragment.graphql new file mode 100644 index 00000000000..7d65789c67b --- /dev/null +++ b/app/assets/javascripts/snippets/fragments/project.fragment.graphql @@ -0,0 +1,6 @@ +fragment Project on Snippet { + project { + fullPath + webUrl + } +}
\ No newline at end of file diff --git a/app/assets/javascripts/snippets/fragments/snippetBase.fragment.graphql b/app/assets/javascripts/snippets/fragments/snippetBase.fragment.graphql new file mode 100644 index 00000000000..57348a422ec --- /dev/null +++ b/app/assets/javascripts/snippets/fragments/snippetBase.fragment.graphql @@ -0,0 +1,13 @@ +fragment SnippetBase on Snippet { + id + title + description + createdAt + updatedAt + visibilityLevel + webUrl + userPermissions { + adminSnippet + updateSnippet + } +}
\ No newline at end of file diff --git a/app/assets/javascripts/snippets/mutations/deleteSnippet.mutation.graphql b/app/assets/javascripts/snippets/mutations/deleteSnippet.mutation.graphql new file mode 100644 index 00000000000..0c829cbdee6 --- /dev/null +++ b/app/assets/javascripts/snippets/mutations/deleteSnippet.mutation.graphql @@ -0,0 +1,5 @@ +mutation DeleteSnippet($id: ID!) { + destroySnippet(input: {id: $id}) { + errors + } +}
\ No newline at end of file diff --git a/app/assets/javascripts/snippets/queries/getSnippet.query.graphql b/app/assets/javascripts/snippets/queries/getSnippet.query.graphql deleted file mode 100644 index 5a5f0d05c5b..00000000000 --- a/app/assets/javascripts/snippets/queries/getSnippet.query.graphql +++ /dev/null @@ -1,13 +0,0 @@ -query getSnippet($ids: [ID!]) { - snippets(ids: $ids) { - edges { - node { - title - description - createdAt - updatedAt - visibility - } - } - } -} diff --git a/app/assets/javascripts/snippets/queries/projectPermissions.query.graphql b/app/assets/javascripts/snippets/queries/projectPermissions.query.graphql new file mode 100644 index 00000000000..288bd0889bf --- /dev/null +++ b/app/assets/javascripts/snippets/queries/projectPermissions.query.graphql @@ -0,0 +1,7 @@ +query CanCreateProjectSnippet($fullPath: ID!) { + project(fullPath: $fullPath) { + userPermissions { + createSnippet + } + } +}
\ No newline at end of file diff --git a/app/assets/javascripts/snippets/queries/snippet.query.graphql b/app/assets/javascripts/snippets/queries/snippet.query.graphql new file mode 100644 index 00000000000..1cb2c86c4d8 --- /dev/null +++ b/app/assets/javascripts/snippets/queries/snippet.query.graphql @@ -0,0 +1,15 @@ +#import '../fragments/snippetBase.fragment.graphql' +#import '../fragments/project.fragment.graphql' +#import '../fragments/author.fragment.graphql' + +query GetSnippetQuery($ids: [ID!]) { + snippets(ids: $ids) { + edges { + node { + ...SnippetBase + ...Project + ...Author + } + } + } +} diff --git a/app/assets/javascripts/snippets/queries/userPermissions.query.graphql b/app/assets/javascripts/snippets/queries/userPermissions.query.graphql new file mode 100644 index 00000000000..f5b97b3d0f0 --- /dev/null +++ b/app/assets/javascripts/snippets/queries/userPermissions.query.graphql @@ -0,0 +1,7 @@ +query CanCreatePersonalSnippet { + currentUser { + userPermissions { + createSnippet + } + } +}
\ No newline at end of file |