diff --git a/Gemfile b/Gemfile index 19dea298c2c..3e01bf6869b 100644 --- a/Gemfile +++ b/Gemfile @@ -608,7 +608,7 @@ gem 'cvss-suite', '~> 3.0.1', require: 'cvss_suite' gem 'arr-pm', '~> 0.0.12' # Remote Development -gem 'devfile', '~> 0.0.19.pre.alpha1' +gem 'devfile', '~> 0.0.20.pre.alpha1' # Apple plist parsing gem 'CFPropertyList', '~> 3.0.0' diff --git a/Gemfile.checksum b/Gemfile.checksum index 65a4045b5b9..333116430ec 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -109,9 +109,9 @@ {"name":"deprecation_toolkit","version":"1.5.1","platform":"ruby","checksum":"a8a1ab1a19ae40ea12560b65010e099f3459ebde390b76621ef0c21c516a04ba"}, {"name":"derailed_benchmarks","version":"2.1.2","platform":"ruby","checksum":"eaadc6206ceeb5538ff8f5e04a0023d54ebdd95d04f33e8960fb95a5f189a14f"}, {"name":"descendants_tracker","version":"0.0.4","platform":"ruby","checksum":"e9c41dd4cfbb85829a9301ea7e7c48c2a03b26f09319db230e6479ccdc780897"}, -{"name":"devfile","version":"0.0.19.pre.alpha1","platform":"arm64-darwin","checksum":"6087103d7e6c6226f2e9209d8619446afdb9bdaaf75cf7f6e06e929622465fe8"}, -{"name":"devfile","version":"0.0.19.pre.alpha1","platform":"ruby","checksum":"d928c4529162c1f2a8434d8509d9d760d15dfb3c133669199de0d12ba3ec9ed8"}, -{"name":"devfile","version":"0.0.19.pre.alpha1","platform":"x86_64-linux","checksum":"8c64e74eb470eedfd9a1754c40f82a6621a26735cdb22fc3d3dad4bc49445b37"}, +{"name":"devfile","version":"0.0.20.pre.alpha1","platform":"arm64-darwin","checksum":"1dcd4a55482f04a0ec308d13fe41358bfbfd95ffe98678a471aa5e0e0c5dba98"}, +{"name":"devfile","version":"0.0.20.pre.alpha1","platform":"ruby","checksum":"6a046be066c1b0392da1387249962d765d1f1a3a242e1ede0459b14a6d6fd760"}, +{"name":"devfile","version":"0.0.20.pre.alpha1","platform":"x86_64-linux","checksum":"d0fa2a05ae7de71a17c6251e853459504ed6a51cb1f90a25ba9d0b7c9cb82074"}, {"name":"device_detector","version":"1.0.0","platform":"ruby","checksum":"b800fb3150b00c23e87b6768011808ac1771fffaae74c3238ebaf2b782947a7d"}, {"name":"devise","version":"4.8.1","platform":"ruby","checksum":"fdd48bbe79a89e7c1152236a70479842ede48bea4fa7f4f2d8da1f872559803e"}, {"name":"devise-two-factor","version":"4.0.2","platform":"ruby","checksum":"6548d2696ed090d27046f888f4fa7380f151e0f823902d46fd9b91e7d0cac511"}, diff --git a/Gemfile.lock b/Gemfile.lock index 75b7953edaf..3add4cdfda3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -418,7 +418,7 @@ GEM thor (>= 0.19, < 2) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - devfile (0.0.19.pre.alpha1) + devfile (0.0.20.pre.alpha1) device_detector (1.0.0) devise (4.8.1) bcrypt (~> 3.0) @@ -1773,7 +1773,7 @@ DEPENDENCIES declarative_policy (~> 1.1.0) deprecation_toolkit (~> 1.5.1) derailed_benchmarks - devfile (~> 0.0.19.pre.alpha1) + devfile (~> 0.0.20.pre.alpha1) device_detector devise (~> 4.8.1) devise-pbkdf2-encryptable (~> 0.0.0)! diff --git a/app/assets/javascripts/admin/abuse_reports/components/abuse_category.vue b/app/assets/javascripts/admin/abuse_reports/components/abuse_category.vue new file mode 100644 index 00000000000..f05f96d6302 --- /dev/null +++ b/app/assets/javascripts/admin/abuse_reports/components/abuse_category.vue @@ -0,0 +1,33 @@ + + + diff --git a/app/assets/javascripts/admin/abuse_reports/components/abuse_report_row.vue b/app/assets/javascripts/admin/abuse_reports/components/abuse_report_row.vue index b8a4640de59..b229dd9e993 100644 --- a/app/assets/javascripts/admin/abuse_reports/components/abuse_report_row.vue +++ b/app/assets/javascripts/admin/abuse_reports/components/abuse_report_row.vue @@ -5,12 +5,14 @@ import { queryToObject } from '~/lib/utils/url_utility'; import { s__, __, sprintf } from '~/locale'; import ListItem from '~/vue_shared/components/registry/list_item.vue'; import { SORT_UPDATED_AT } from '../constants'; +import AbuseCategory from './abuse_category.vue'; export default { name: 'AbuseReportRow', components: { GlLink, ListItem, + AbuseCategory, }, props: { report: { @@ -44,13 +46,24 @@ export default { diff --git a/app/assets/javascripts/admin/abuse_reports/constants.js b/app/assets/javascripts/admin/abuse_reports/constants.js index 9458aea299e..acb79293dfb 100644 --- a/app/assets/javascripts/admin/abuse_reports/constants.js +++ b/app/assets/javascripts/admin/abuse_reports/constants.js @@ -5,7 +5,7 @@ import { OPERATORS_IS, TOKEN_TITLE_STATUS, } from '~/vue_shared/components/filtered_search_bar/constants'; -import { __ } from '~/locale'; +import { s__, __ } from '~/locale'; const STATUS_OPTIONS = [ { value: 'closed', title: __('Closed') }, @@ -78,3 +78,46 @@ export const FILTERED_SEARCH_TOKENS = [ FILTERED_SEARCH_TOKEN_REPORTER, FILTERED_SEARCH_TOKEN_STATUS, ]; + +export const ABUSE_CATEGORIES = { + spam: { + backgroundColor: '#f5d9a8', + color: 'orange-700', + title: s__('AbuseReport|Spam'), + }, + offensive: { + backgroundColor: '#e1d8f9', + color: 'purple-700', + title: s__('AbuseReport|Offensive or Abusive'), + }, + phishing: { + backgroundColor: '#7c7ccc', + color: 'indigo-800', + title: s__('AbuseReport|Phishing'), + }, + crypto: { + backgroundColor: '#fdd4cd', + color: 'red-700', + title: s__('AbuseReport|Crypto Mining'), + }, + credentials: { + backgroundColor: '#cbe2f9', + color: 'blue-700', + title: s__('AbuseReport|Personal information or credentials'), + }, + copyright: { + backgroundColor: '#c3e6cd', + color: 'green-700', + title: s__('AbuseReport|Copyright or trademark violation'), + }, + malware: { + backgroundColor: '#fdd4cd', + color: 'red-700', + title: s__('AbuseReport|Malware'), + }, + other: { + backgroundColor: '#dcdcde', + color: 'gray-700', + title: s__('AbuseReport|Other'), + }, +}; diff --git a/app/assets/javascripts/graphql_shared/issuable_client.js b/app/assets/javascripts/graphql_shared/issuable_client.js index d0a88f2e0a6..08733bbe620 100644 --- a/app/assets/javascripts/graphql_shared/issuable_client.js +++ b/app/assets/javascripts/graphql_shared/issuable_client.js @@ -6,7 +6,7 @@ import errorQuery from '~/boards/graphql/client/error.query.graphql'; import getIssueStateQuery from '~/issues/show/queries/get_issue_state.query.graphql'; import createDefaultClient from '~/lib/graphql'; import typeDefs from '~/work_items/graphql/typedefs.graphql'; -import { WIDGET_TYPE_NOTES } from '~/work_items/constants'; +import { WIDGET_TYPE_NOTES, WIDGET_TYPE_AWARD_EMOJI } from '~/work_items/constants'; import activeBoardItemQuery from 'ee_else_ce/boards/graphql/client/active_board_item.query.graphql'; export const config = { @@ -36,6 +36,15 @@ export const config = { }, }, }, + WorkItemWidgetAwardEmoji: { + fields: { + // If we add any key args, the awardEmoji field becomes awardEmoji({"first":10}) and + // kills any possibility to handle it on the widget level without hardcoding a string. + awardEmoji: { + keyArgs: false, + }, + }, + }, WorkItemWidgetProgress: { fields: { progress: { @@ -68,10 +77,30 @@ export const config = { const incomingWidget = incoming.find( (w) => w.type && w.type === existingWidget.type, ); - // We don't want to override existing notes with empty widget on work item updates - if (incomingWidget?.type === WIDGET_TYPE_NOTES && !context.variables.pageSize) { + // We don't want to override existing notes or award emojis with empty widget on work item updates + if ( + (incomingWidget?.type === WIDGET_TYPE_NOTES || + incomingWidget?.type === WIDGET_TYPE_AWARD_EMOJI) && + !context.variables.pageSize + ) { return existingWidget; } + + // we want to concat next page of awardEmoji to the existing ones + if (incomingWidget?.type === WIDGET_TYPE_AWARD_EMOJI && context.variables.after) { + // concatPagination won't work because we were placing new widget here so we have to do this manually + return { + ...incomingWidget, + awardEmoji: { + ...incomingWidget.awardEmoji, + nodes: [ + ...existingWidget.awardEmoji.nodes, + ...incomingWidget.awardEmoji.nodes, + ], + }, + }; + } + // we want to concat next page of discussions to the existing ones if (incomingWidget?.type === WIDGET_TYPE_NOTES && context.variables.after) { // concatPagination won't work because we were placing new widget here so we have to do this manually diff --git a/app/assets/javascripts/work_items/components/work_item_assignees.vue b/app/assets/javascripts/work_items/components/work_item_assignees.vue index ad9db1428ec..f7ac63e16c3 100644 --- a/app/assets/javascripts/work_items/components/work_item_assignees.vue +++ b/app/assets/javascripts/work_items/components/work_item_assignees.vue @@ -340,7 +340,7 @@ export default { class="assign-myself" data-testid="assign-self" @click.stop="assignToCurrentUser" - >{{ __('Assign myself') }}{{ __('Assign yourself') }} diff --git a/app/assets/javascripts/work_items/components/work_item_award_emoji.vue b/app/assets/javascripts/work_items/components/work_item_award_emoji.vue index 144c29b8ec3..3dd3a072d0f 100644 --- a/app/assets/javascripts/work_items/components/work_item_award_emoji.vue +++ b/app/assets/javascripts/work_items/components/work_item_award_emoji.vue @@ -7,9 +7,15 @@ import AwardsList from '~/vue_shared/components/awards_list.vue'; import { isLoggedIn } from '~/lib/utils/common_utils'; import { TYPENAME_USER } from '~/graphql_shared/constants'; +import workItemAwardEmojiQuery from '../graphql/award_emoji.query.graphql'; import updateAwardEmojiMutation from '../graphql/update_award_emoji.mutation.graphql'; -import workItemByIidQuery from '../graphql/work_item_by_iid.query.graphql'; -import { EMOJI_THUMBSDOWN, EMOJI_THUMBSUP, WIDGET_TYPE_AWARD_EMOJI } from '../constants'; +import { + EMOJI_THUMBSDOWN, + EMOJI_THUMBSUP, + WIDGET_TYPE_AWARD_EMOJI, + DEFAULT_PAGE_SIZE_EMOJIS, + I18N_WORK_ITEM_FETCH_AWARD_EMOJI_ERROR, +} from '../constants'; export default { defaultAwards: [EMOJI_THUMBSUP, EMOJI_THUMBSDOWN], @@ -26,16 +32,17 @@ export default { type: String, required: true, }, - awardEmoji: { - type: Object, - required: true, - }, workItemIid: { type: String, required: false, default: null, }, }, + data() { + return { + isLoading: false, + }; + }, computed: { currentUserId() { return window.gon.current_user_id; @@ -47,6 +54,10 @@ export default { * Parse and convert award emoji list to a format that AwardsList can understand */ awards() { + if (!this.awardEmoji) { + return []; + } + return this.awardEmoji.nodes.map((emoji) => ({ name: emoji.name, user: { @@ -55,16 +66,56 @@ export default { }, })); }, + pageInfo() { + return this.awardEmoji?.pageInfo; + }, + hasNextPage() { + return this.pageInfo?.hasNextPage; + }, + }, + apollo: { + awardEmoji: { + query: workItemAwardEmojiQuery, + variables() { + return { + iid: this.workItemIid, + fullPath: this.workItemFullpath, + after: this.after, + pageSize: DEFAULT_PAGE_SIZE_EMOJIS, + }; + }, + update(data) { + const widgets = data.workspace?.workItems?.nodes[0].widgets; + return widgets?.find((widget) => widget.type === WIDGET_TYPE_AWARD_EMOJI).awardEmoji || {}; + }, + skip() { + return !this.workItemIid; + }, + result() { + if (this.hasNextPage) { + this.fetchAwardEmojis(); + } else { + this.isLoading = false; + } + }, + error() { + this.$emit('error', I18N_WORK_ITEM_FETCH_AWARD_EMOJI_ERROR); + }, + }, }, methods: { - getAwards() { - return this.awardEmoji.nodes.map((emoji) => ({ - name: emoji.name, - user: { - id: getIdFromGraphQLId(emoji.user.id), - name: emoji.user.name, - }, - })); + async fetchAwardEmojis() { + this.isLoading = true; + try { + await this.$apollo.queries.awardEmoji.fetchMore({ + variables: { + pageSize: DEFAULT_PAGE_SIZE_EMOJIS, + after: this.pageInfo?.endCursor, + }, + }); + } catch (error) { + this.$emit('error', I18N_WORK_ITEM_FETCH_AWARD_EMOJI_ERROR); + } }, isEmojiPresentForCurrentUser(name) { return ( @@ -108,8 +159,12 @@ export default { }, updateWorkItemAwardEmojiWidgetCache({ cache, name, toggledOn }) { const query = { - query: workItemByIidQuery, - variables: { fullPath: this.workItemFullpath, iid: this.workItemIid }, + query: workItemAwardEmojiQuery, + variables: { + fullPath: this.workItemFullpath, + iid: this.workItemIid, + pageSize: DEFAULT_PAGE_SIZE_EMOJIS, + }, }; const sourceData = cache.readQuery(query); @@ -117,7 +172,6 @@ export default { const newData = produce(sourceData, (draftState) => { const { widgets } = draftState.workspace.workItems.nodes[0]; const widgetAwardEmoji = widgets.find((widget) => widget.type === WIDGET_TYPE_AWARD_EMOJI); - widgetAwardEmoji.awardEmoji.nodes = this.getAwardEmojiNodes(name, toggledOn); }); @@ -175,7 +229,7 @@ export default {