diff options
Diffstat (limited to 'app/assets/javascripts/emoji')
7 files changed, 148 insertions, 2 deletions
diff --git a/app/assets/javascripts/emoji/awards_app/index.js b/app/assets/javascripts/emoji/awards_app/index.js new file mode 100644 index 00000000000..16268910f49 --- /dev/null +++ b/app/assets/javascripts/emoji/awards_app/index.js @@ -0,0 +1,43 @@ +import Vue from 'vue'; +import { mapActions, mapState } from 'vuex'; +import { parseBoolean } from '~/lib/utils/common_utils'; +import AwardsList from '~/vue_shared/components/awards_list.vue'; +import createstore from './store'; + +export default (el) => { + const { + dataset: { path }, + } = el; + const canAwardEmoji = parseBoolean(el.dataset.canAwardEmoji); + + return new Vue({ + el, + store: createstore(), + computed: { + ...mapState(['currentUserId', 'canAwardEmoji', 'awards']), + }, + created() { + this.setInitialData({ path, currentUserId: window.gon.current_user_id, canAwardEmoji }); + }, + mounted() { + this.fetchAwards(); + }, + methods: { + ...mapActions(['setInitialData', 'fetchAwards', 'toggleAward']), + }, + render(createElement) { + return createElement(AwardsList, { + props: { + awards: this.awards, + canAwardEmoji: this.canAwardEmoji, + currentUserId: this.currentUserId, + defaultAwards: ['thumbsup', 'thumbsdown'], + selectedClass: 'gl-bg-blue-50! is-active', + }, + on: { + award: this.toggleAward, + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/emoji/awards_app/store/actions.js b/app/assets/javascripts/emoji/awards_app/store/actions.js new file mode 100644 index 00000000000..482acc5a3a9 --- /dev/null +++ b/app/assets/javascripts/emoji/awards_app/store/actions.js @@ -0,0 +1,51 @@ +import * as Sentry from '@sentry/browser'; +import axios from '~/lib/utils/axios_utils'; +import { normalizeHeaders } from '~/lib/utils/common_utils'; +import { __ } from '~/locale'; +import showToast from '~/vue_shared/plugins/global_toast'; +import { + SET_INITIAL_DATA, + FETCH_AWARDS_SUCCESS, + ADD_NEW_AWARD, + REMOVE_AWARD, +} from './mutation_types'; + +export const setInitialData = ({ commit }, data) => commit(SET_INITIAL_DATA, data); + +export const fetchAwards = async ({ commit, dispatch, state }, page = '1') => { + try { + const { data, headers } = await axios.get(state.path, { params: { per_page: 100, page } }); + const normalizedHeaders = normalizeHeaders(headers); + const nextPage = normalizedHeaders['X-NEXT-PAGE']; + + commit(FETCH_AWARDS_SUCCESS, data); + + if (nextPage) { + dispatch('fetchAwards', nextPage); + } + } catch (error) { + Sentry.captureException(error); + } +}; + +export const toggleAward = async ({ commit, state }, name) => { + const award = state.awards.find((a) => a.name === name && a.user.id === state.currentUserId); + + try { + if (award) { + await axios.delete(`${state.path}/${award.id}`); + + commit(REMOVE_AWARD, award.id); + + showToast(__('Award removed')); + } else { + const { data } = await axios.post(state.path, { name }); + + commit(ADD_NEW_AWARD, data); + + showToast(__('Award added')); + } + } catch (error) { + Sentry.captureException(error); + } +}; diff --git a/app/assets/javascripts/emoji/awards_app/store/index.js b/app/assets/javascripts/emoji/awards_app/store/index.js new file mode 100644 index 00000000000..53ed50f9f5d --- /dev/null +++ b/app/assets/javascripts/emoji/awards_app/store/index.js @@ -0,0 +1,20 @@ +import Vue from 'vue'; +import Vuex from 'vuex'; +import * as actions from './actions'; +import mutations from './mutations'; + +Vue.use(Vuex); + +const createState = () => ({ + awards: [], + awardPath: '', + currentUserId: null, + canAwardEmoji: false, +}); + +export default () => + new Vuex.Store({ + state: createState(), + actions, + mutations, + }); diff --git a/app/assets/javascripts/emoji/awards_app/store/mutation_types.js b/app/assets/javascripts/emoji/awards_app/store/mutation_types.js new file mode 100644 index 00000000000..af6289d0943 --- /dev/null +++ b/app/assets/javascripts/emoji/awards_app/store/mutation_types.js @@ -0,0 +1,6 @@ +export const SET_INITIAL_DATA = 'SET_INITIAL_DATA'; + +export const FETCH_AWARDS_SUCCESS = 'FETCH_AWARDS_SUCCESS'; + +export const ADD_NEW_AWARD = 'ADD_NEW_AWARD'; +export const REMOVE_AWARD = 'REMOVE_AWARD'; diff --git a/app/assets/javascripts/emoji/awards_app/store/mutations.js b/app/assets/javascripts/emoji/awards_app/store/mutations.js new file mode 100644 index 00000000000..8edcfa92885 --- /dev/null +++ b/app/assets/javascripts/emoji/awards_app/store/mutations.js @@ -0,0 +1,23 @@ +import { + SET_INITIAL_DATA, + FETCH_AWARDS_SUCCESS, + ADD_NEW_AWARD, + REMOVE_AWARD, +} from './mutation_types'; + +export default { + [SET_INITIAL_DATA](state, { path, currentUserId, canAwardEmoji }) { + state.path = path; + state.currentUserId = currentUserId; + state.canAwardEmoji = canAwardEmoji; + }, + [FETCH_AWARDS_SUCCESS](state, data) { + state.awards.push(...data); + }, + [ADD_NEW_AWARD](state, data) { + state.awards.push(data); + }, + [REMOVE_AWARD](state, awardId) { + state.awards = state.awards.filter(({ id }) => id !== awardId); + }, +}; diff --git a/app/assets/javascripts/emoji/components/category.vue b/app/assets/javascripts/emoji/components/category.vue index db6ead3ff69..39881979c4f 100644 --- a/app/assets/javascripts/emoji/components/category.vue +++ b/app/assets/javascripts/emoji/components/category.vue @@ -39,7 +39,7 @@ export default { <template> <gl-intersection-observer class="gl-px-5 gl-h-full" @appear="categoryAppeared"> - <div class="gl-top-0 gl-py-3 gl-w-full emoji-picker-category-header"> + <div class="gl-top-0 gl-py-3 gl-w-full gl-z-index-1 emoji-picker-category-header"> <b>{{ categoryTitle }}</b> </div> <template v-if="emojis.length"> diff --git a/app/assets/javascripts/emoji/components/picker.vue b/app/assets/javascripts/emoji/components/picker.vue index 37f3433b781..71cabe80529 100644 --- a/app/assets/javascripts/emoji/components/picker.vue +++ b/app/assets/javascripts/emoji/components/picker.vue @@ -82,6 +82,8 @@ export default { no-flip right lazy + @shown="$emit('shown')" + @hidden="$emit('hidden')" > <template #button-content><slot name="button-content"></slot></template> <gl-search-box-by-type @@ -99,10 +101,11 @@ export default { v-for="(category, index) in categoryNames" :key="category.name" :class="{ - 'gl-text-black-normal! emoji-picker-category-active': index === currentCategory, + 'gl-text-body! emoji-picker-category-active': index === currentCategory, }" type="button" class="gl-border-0 gl-border-b-2 gl-border-b-solid gl-flex-fill-1 gl-text-gray-300 gl-pt-3 gl-pb-3 gl-bg-transparent emoji-picker-category-tab" + :aria-label="category.name" @click="scrollToCategory(category.name)" > <gl-icon :name="category.icon" :size="12" /> |