summaryrefslogtreecommitdiff
path: root/app/assets/javascripts
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-04-01 15:14:27 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-04-01 15:14:27 +0000
commitafbaf78b0d819326741a01b093bdbc4702570417 (patch)
treea16a6f2a8fcc18e60f25ac72df6ab22cfe0eae79 /app/assets/javascripts
parent38b3003b67db3f2eadfa81fd28b13d168f665766 (diff)
downloadgitlab-ce-afbaf78b0d819326741a01b093bdbc4702570417.tar.gz
Add latest changes from gitlab-org/gitlab@13-10-stable-ee
Diffstat (limited to 'app/assets/javascripts')
-rw-r--r--app/assets/javascripts/emoji/components/category.vue7
-rw-r--r--app/assets/javascripts/emoji/components/picker.vue38
-rw-r--r--app/assets/javascripts/emoji/components/utils.js69
-rw-r--r--app/assets/javascripts/emoji/constants.js4
-rw-r--r--app/assets/javascripts/emoji/index.js5
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue4
6 files changed, 93 insertions, 34 deletions
diff --git a/app/assets/javascripts/emoji/components/category.vue b/app/assets/javascripts/emoji/components/category.vue
index a11122d5403..db6ead3ff69 100644
--- a/app/assets/javascripts/emoji/components/category.vue
+++ b/app/assets/javascripts/emoji/components/category.vue
@@ -1,6 +1,6 @@
<script>
import { GlIntersectionObserver } from '@gitlab/ui';
-import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
+import { humanize } from '~/lib/utils/text_utility';
import EmojiGroup from './emoji_group.vue';
export default {
@@ -25,7 +25,7 @@ export default {
},
computed: {
categoryTitle() {
- return capitalizeFirstCharacter(this.category);
+ return humanize(this.category);
},
},
methods: {
@@ -33,9 +33,6 @@ export default {
this.renderGroup = true;
this.$emit('appear', this.category);
},
- categoryDissappeared() {
- this.renderGroup = false;
- },
},
};
</script>
diff --git a/app/assets/javascripts/emoji/components/picker.vue b/app/assets/javascripts/emoji/components/picker.vue
index 7cd20d82329..37f3433b781 100644
--- a/app/assets/javascripts/emoji/components/picker.vue
+++ b/app/assets/javascripts/emoji/components/picker.vue
@@ -1,11 +1,12 @@
<script>
import { GlIcon, GlDropdown, GlSearchBoxByType } from '@gitlab/ui';
+import { findLastIndex } from 'lodash';
import VirtualList from 'vue-virtual-scroll-list';
import { CATEGORY_NAMES } from '~/emoji';
-import { CATEGORY_ICON_MAP } from '../constants';
+import { CATEGORY_ICON_MAP, FREQUENTLY_USED_KEY } from '../constants';
import Category from './category.vue';
import EmojiList from './emoji_list.vue';
-import { getEmojiCategories } from './utils';
+import { addToFrequentlyUsed, getEmojiCategories, hasFrequentlyUsedEmojis } from './utils';
export default {
components: {
@@ -25,13 +26,16 @@ export default {
},
data() {
return {
- currentCategory: null,
+ currentCategory: 0,
searchValue: '',
};
},
computed: {
categoryNames() {
- return CATEGORY_NAMES.map((category) => ({
+ return CATEGORY_NAMES.filter((c) => {
+ if (c === FREQUENTLY_USED_KEY) return hasFrequentlyUsedEmojis();
+ return true;
+ }).map((category) => ({
name: category,
icon: CATEGORY_ICON_MAP[category],
}));
@@ -50,6 +54,7 @@ export default {
selectEmoji(name) {
this.$emit('click', name);
this.$refs.dropdown.hide();
+ addToFrequentlyUsed(name);
},
getBoundaryElement() {
return document.querySelector('.content-wrapper') || 'scrollParent';
@@ -58,6 +63,11 @@ export default {
this.$refs.virtualScoller.setScrollTop(0);
this.$refs.virtualScoller.forceRender();
},
+ async onScroll(event, { offset }) {
+ const categories = await getEmojiCategories();
+
+ this.currentCategory = findLastIndex(Object.values(categories), ({ top }) => offset >= top);
+ },
},
};
</script>
@@ -86,10 +96,10 @@ export default {
class="gl-display-flex gl-mx-5 gl-border-b-solid gl-border-gray-100 gl-border-b-1"
>
<button
- v-for="category in categoryNames"
+ v-for="(category, index) in categoryNames"
:key="category.name"
:class="{
- 'gl-text-black-normal! emoji-picker-category-active': category.name === currentCategory,
+ 'gl-text-black-normal! 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"
@@ -100,18 +110,20 @@ export default {
</div>
<emoji-list :search-value="searchValue">
<template #default="{ filteredCategories }">
- <virtual-list ref="virtualScoller" :size="258" :remain="1" :bench="2" variable>
+ <virtual-list
+ ref="virtualScoller"
+ :size="258"
+ :remain="1"
+ :bench="2"
+ variable
+ :onscroll="onScroll"
+ >
<div
v-for="(category, categoryKey) in filteredCategories"
:key="categoryKey"
:style="{ height: category.height + 'px' }"
>
- <category
- :category="categoryKey"
- :emojis="category.emojis"
- @appear="categoryAppeared"
- @click="selectEmoji"
- />
+ <category :category="categoryKey" :emojis="category.emojis" @click="selectEmoji" />
</div>
</virtual-list>
</template>
diff --git a/app/assets/javascripts/emoji/components/utils.js b/app/assets/javascripts/emoji/components/utils.js
index b95b56a1d6f..3465a8ae7e6 100644
--- a/app/assets/javascripts/emoji/components/utils.js
+++ b/app/assets/javascripts/emoji/components/utils.js
@@ -1,27 +1,68 @@
-import { chunk, memoize } from 'lodash';
+import Cookies from 'js-cookie';
+import { chunk, memoize, uniq } from 'lodash';
import { initEmojiMap, getEmojiCategoryMap } from '~/emoji';
-import { EMOJIS_PER_ROW, EMOJI_ROW_HEIGHT, CATEGORY_ROW_HEIGHT } from '../constants';
+import {
+ EMOJIS_PER_ROW,
+ EMOJI_ROW_HEIGHT,
+ CATEGORY_ROW_HEIGHT,
+ FREQUENTLY_USED_KEY,
+ FREQUENTLY_USED_COOKIE_KEY,
+} from '../constants';
export const generateCategoryHeight = (emojisLength) =>
emojisLength * EMOJI_ROW_HEIGHT + CATEGORY_ROW_HEIGHT;
+export const getFrequentlyUsedEmojis = () => {
+ const savedEmojis = Cookies.get(FREQUENTLY_USED_COOKIE_KEY);
+
+ if (!savedEmojis) return null;
+
+ const emojis = chunk(uniq(savedEmojis.split(',')), 9);
+
+ return {
+ frequently_used: {
+ emojis,
+ top: 0,
+ height: generateCategoryHeight(emojis.length),
+ },
+ };
+};
+
+export const addToFrequentlyUsed = (emoji) => {
+ const frequentlyUsedEmojis = uniq(
+ (Cookies.get(FREQUENTLY_USED_COOKIE_KEY) || '')
+ .split(',')
+ .filter((e) => e)
+ .concat(emoji),
+ );
+
+ Cookies.set(FREQUENTLY_USED_COOKIE_KEY, frequentlyUsedEmojis.join(','), { expires: 365 });
+};
+
+export const hasFrequentlyUsedEmojis = () => getFrequentlyUsedEmojis() !== null;
+
export const getEmojiCategories = memoize(async () => {
await initEmojiMap();
const categories = await getEmojiCategoryMap();
- let top = 0;
+ const frequentlyUsedEmojis = getFrequentlyUsedEmojis();
+ let top = frequentlyUsedEmojis
+ ? frequentlyUsedEmojis.frequently_used.top + frequentlyUsedEmojis.frequently_used.height
+ : 0;
return Object.freeze(
- Object.keys(categories).reduce((acc, category) => {
- const emojis = chunk(categories[category], EMOJIS_PER_ROW);
- const height = generateCategoryHeight(emojis.length);
- const newAcc = {
- ...acc,
- [category]: { emojis, height, top },
- };
- top += height;
-
- return newAcc;
- }, {}),
+ Object.keys(categories)
+ .filter((c) => c !== FREQUENTLY_USED_KEY)
+ .reduce((acc, category) => {
+ const emojis = chunk(categories[category], EMOJIS_PER_ROW);
+ const height = generateCategoryHeight(emojis.length);
+ const newAcc = {
+ ...acc,
+ [category]: { emojis, height, top },
+ };
+ top += height;
+
+ return newAcc;
+ }, frequentlyUsedEmojis || {}),
);
});
diff --git a/app/assets/javascripts/emoji/constants.js b/app/assets/javascripts/emoji/constants.js
index bf73d1ca5a9..e9f2272e759 100644
--- a/app/assets/javascripts/emoji/constants.js
+++ b/app/assets/javascripts/emoji/constants.js
@@ -1,4 +1,8 @@
+export const FREQUENTLY_USED_KEY = 'frequently_used';
+export const FREQUENTLY_USED_COOKIE_KEY = 'frequently_used_emojis';
+
export const CATEGORY_ICON_MAP = {
+ [FREQUENTLY_USED_KEY]: 'history',
activity: 'dumbbell',
people: 'smiley',
nature: 'nature',
diff --git a/app/assets/javascripts/emoji/index.js b/app/assets/javascripts/emoji/index.js
index d3b658a4020..7faf0fe5f08 100644
--- a/app/assets/javascripts/emoji/index.js
+++ b/app/assets/javascripts/emoji/index.js
@@ -2,7 +2,7 @@ import { escape, minBy } from 'lodash';
import emojiAliases from 'emojis/aliases.json';
import AccessorUtilities from '../lib/utils/accessor';
import axios from '../lib/utils/axios_utils';
-import { CATEGORY_ICON_MAP } from './constants';
+import { CATEGORY_ICON_MAP, FREQUENTLY_USED_KEY } from './constants';
let emojiMap = null;
let validEmojiNames = null;
@@ -162,6 +162,9 @@ let emojiCategoryMap;
export function getEmojiCategoryMap() {
if (!emojiCategoryMap) {
emojiCategoryMap = CATEGORY_NAMES.reduce((acc, category) => {
+ if (category === FREQUENTLY_USED_KEY) {
+ return acc;
+ }
return { ...acc, [category]: [] };
}, {});
Object.keys(emojiMap).forEach((name) => {
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
index a49eb7fd611..04ab0fd00aa 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
@@ -3,6 +3,8 @@ import { throttle } from 'lodash';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { encodeSaferUrl } from '~/lib/utils/url_utility';
+const BLOB_PREFIX = 'blob:';
+
export default {
props: {
path: {
@@ -45,7 +47,7 @@ export default {
return this.width && this.height;
},
safePath() {
- return encodeSaferUrl(this.path);
+ return this.path.startsWith(BLOB_PREFIX) ? this.path : encodeSaferUrl(this.path);
},
},
beforeDestroy() {