diff options
Diffstat (limited to 'app')
18 files changed, 134 insertions, 37 deletions
diff --git a/app/assets/javascripts/graphql_shared/issuable_client.js b/app/assets/javascripts/graphql_shared/issuable_client.js index 9537c9ef8a6..d0ba34b6127 100644 --- a/app/assets/javascripts/graphql_shared/issuable_client.js +++ b/app/assets/javascripts/graphql_shared/issuable_client.js @@ -146,7 +146,7 @@ export const config = { }, IssueConnection: { merge(existing = { nodes: [] }, incoming, { args }) { - if (!args.after) { + if (!args?.after) { return incoming; } return { diff --git a/app/assets/javascripts/graphql_shared/possible_types.json b/app/assets/javascripts/graphql_shared/possible_types.json index ee5f8c2ac2f..4ef0d067030 100644 --- a/app/assets/javascripts/graphql_shared/possible_types.json +++ b/app/assets/javascripts/graphql_shared/possible_types.json @@ -150,6 +150,7 @@ "User": [ "AddOnUser", "AutocompletedUser", + "CurrentUser", "MergeRequestAssignee", "MergeRequestAuthor", "MergeRequestParticipant", diff --git a/app/assets/javascripts/lib/utils/datetime/locale_dateformat.js b/app/assets/javascripts/lib/utils/datetime/locale_dateformat.js index 81922c4da1d..f1446fa5ac4 100644 --- a/app/assets/javascripts/lib/utils/datetime/locale_dateformat.js +++ b/app/assets/javascripts/lib/utils/datetime/locale_dateformat.js @@ -3,25 +3,36 @@ import { createDateTimeFormat } from '~/locale'; /** * Format a Date with the help of {@link DateTimeFormat.asDateTime} * - * Note: In case you can use localDateFormat.asDateTime directly, please do that. + * Note: In case you can use localeDateFormat.asDateTime directly, please do that. * * @example - * localDateFormat[DATE_WITH_TIME_FORMAT].format(date) // returns 'Jul 6, 2020, 2:43 PM' - * localDateFormat[DATE_WITH_TIME_FORMAT].formatRange(date, date) // returns 'Jul 6, 2020, 2:45PM – 8:43 PM' + * localeDateFormat[DATE_WITH_TIME_FORMAT].format(date) // returns 'Jul 6, 2020, 2:43 PM' + * localeDateFormat[DATE_WITH_TIME_FORMAT].formatRange(date, date) // returns 'Jul 6, 2020, 2:45PM – 8:43 PM' */ export const DATE_WITH_TIME_FORMAT = 'asDateTime'; + +/** + * Format a Date with the help of {@link DateTimeFormat.asDateTimeFull} + * + * Note: In case you can use localeDateFormat.asDateTimeFull directly, please do that. + * + * @example + * localeDateFormat[DATE_TIME_FULL_FORMAT].format(date) // returns 'July 6, 2020 at 2:43:12 PM GMT' + */ +export const DATE_TIME_FULL_FORMAT = 'asDateTimeFull'; + /** * Format a Date with the help of {@link DateTimeFormat.asDate} * - * Note: In case you can use localDateFormat.asDate directly, please do that. + * Note: In case you can use localeDateFormat.asDate directly, please do that. * * @example - * localDateFormat[DATE_ONLY_FORMAT].format(date) // returns 'Jul 05, 2023' - * localDateFormat[DATE_ONLY_FORMAT].formatRange(date, date) // returns 'Jul 05 - Jul 07, 2023' + * localeDateFormat[DATE_ONLY_FORMAT].format(date) // returns 'Jul 05, 2023' + * localeDateFormat[DATE_ONLY_FORMAT].formatRange(date, date) // returns 'Jul 05 - Jul 07, 2023' */ export const DATE_ONLY_FORMAT = 'asDate'; export const DEFAULT_DATE_TIME_FORMAT = DATE_WITH_TIME_FORMAT; -export const DATE_TIME_FORMATS = [DATE_WITH_TIME_FORMAT, DATE_ONLY_FORMAT]; +export const DATE_TIME_FORMATS = [DATE_WITH_TIME_FORMAT, DATE_TIME_FULL_FORMAT, DATE_ONLY_FORMAT]; /** * The DateTimeFormat utilities support formatting a number of types, @@ -54,7 +65,7 @@ class DateTimeFormat { * @example * // en-US: returns something like Jul 6, 2020, 2:43 PM * // en-GB: returns something like 6 Jul 2020, 14:43 - * localDateFormat.asDateTime.format(date) + * localeDateFormat.asDateTime.format(date) * * @returns {DateTimeFormatter} */ @@ -68,6 +79,32 @@ class DateTimeFormat { }) ); } + /** + * Locale aware formatter to a complete date time. + * + * This is needed if you need to convey a full timestamp including timezone and seconds. + * + * This is mainly used in tooltips. Use {@link DateTimeFormat.asDateTime} + * if you don't need to show all the information. + * + * + * @example + * // en-US: returns something like July 6, 2020 at 2:43:12 PM GMT + * // en-GB: returns something like 6 July 2020 at 14:43:12 GMT + * localeDateFormat.asDateTimeFull.format(date) + * + * @returns {DateTimeFormatter} + */ + get asDateTimeFull() { + return ( + this.#formatters[DATE_TIME_FULL_FORMAT] || + this.#createFormatter(DATE_TIME_FULL_FORMAT, { + dateStyle: 'long', + timeStyle: 'long', + hourCycle: DateTimeFormat.#hourCycle, + }) + ); + } /** * Locale aware formatter to display a only the date. @@ -77,12 +114,12 @@ class DateTimeFormat { * @example * // en-US: returns something like Jul 6, 2020 * // en-GB: returns something like 6 Jul 2020 - * localDateFormat.asDate.format(date) + * localeDateFormat.asDate.format(date) * * @example * // en-US: returns something like Jul 6 – 7, 2020 * // en-GB: returns something like 6-7 Jul 2020 - * localDateFormat.asDate.formatRange(date, date2) + * localeDateFormat.asDate.formatRange(date, date2) * * @returns {DateTimeFormatter} */ @@ -177,6 +214,7 @@ class DateTimeFormat { * * DateTime (showing both date and times): * - {@link DateTimeFormat.asDateTime localeDateFormat.asDateTime} - the default format for date times + * - {@link DateTimeFormat.asDateTimeFull localeDateFormat.asDateTimeFull} - full format, including timezone and seconds * * Date (showing date only): * - {@link DateTimeFormat.asDate localeDateFormat.asDate} - the default format for a date diff --git a/app/assets/javascripts/lib/utils/datetime/timeago_utility.js b/app/assets/javascripts/lib/utils/datetime/timeago_utility.js index a25acd5c711..3a94b26ee35 100644 --- a/app/assets/javascripts/lib/utils/datetime/timeago_utility.js +++ b/app/assets/javascripts/lib/utils/datetime/timeago_utility.js @@ -1,7 +1,6 @@ import * as timeago from 'timeago.js'; import { languageCode, s__ } from '~/locale'; import { DEFAULT_DATE_TIME_FORMAT, localeDateFormat } from '~/lib/utils/datetime/locale_dateformat'; -import { formatDate } from './date_format_utility'; /** * Timeago uses underscores instead of dashes to separate language from country code. @@ -130,7 +129,7 @@ export const localTimeAgo = (elements, updateTooltip = true) => { function addTimeAgoTooltip() { elements.forEach((el) => { // Recreate with custom template - el.setAttribute('title', formatDate(el.dateTime)); + el.setAttribute('title', localeDateFormat.asDateTimeFull.format(el.dateTime)); }); } diff --git a/app/assets/javascripts/search/sidebar/components/archived_filter/index.vue b/app/assets/javascripts/search/sidebar/components/archived_filter/index.vue index 914ff99075b..0308db17dc4 100644 --- a/app/assets/javascripts/search/sidebar/components/archived_filter/index.vue +++ b/app/assets/javascripts/search/sidebar/components/archived_filter/index.vue @@ -48,9 +48,9 @@ export default { <template> <gl-form-checkbox-group v-model="selectedFilter"> - <h5 class="gl-mt-0 gl-mb-5 gl-font-sm"> + <div class="gl-mb-2 gl-font-weight-bold gl-font-sm" data-testid="archived-filter-title"> {{ $options.archivedFilterData.headerLabel }} - </h5> + </div> <gl-form-checkbox class="gl-flex-grow-1 gl-display-inline-flex gl-justify-content-space-between gl-w-full" :class="$options.LABEL_DEFAULT_CLASSES" diff --git a/app/assets/javascripts/search/sidebar/components/filters_template.vue b/app/assets/javascripts/search/sidebar/components/filters_template.vue index a3aa392d7fc..2f40a430bfa 100644 --- a/app/assets/javascripts/search/sidebar/components/filters_template.vue +++ b/app/assets/javascripts/search/sidebar/components/filters_template.vue @@ -40,7 +40,11 @@ export default { </script> <template> - <gl-form class="issue-filters gl-px-5 gl-pt-0" @submit.prevent="applyQueryWithTracking"> + <gl-form + class="issue-filters gl-px-5 gl-pt-0" + :aria-label="__('Search filters')" + @submit.prevent="applyQueryWithTracking" + > <slot></slot> <div class="gl-display-flex gl-align-items-center gl-mt-4"> <gl-button category="primary" variant="confirm" type="submit" :disabled="!sidebarDirty"> diff --git a/app/assets/javascripts/search/sidebar/components/label_filter/index.vue b/app/assets/javascripts/search/sidebar/components/label_filter/index.vue index a53f519161b..106093b5ad1 100644 --- a/app/assets/javascripts/search/sidebar/components/label_filter/index.vue +++ b/app/assets/javascripts/search/sidebar/components/label_filter/index.vue @@ -179,10 +179,10 @@ export default { <template> <div class="gl-pb-0 gl-md-pt-0 label-filter gl-relative"> - <h5 class="gl-my-0 gl-font-sm" data-testid="label-filter-title"> + <div class="gl-mb-2 gl-font-weight-bold gl-font-sm" data-testid="label-filter-title"> {{ $options.labelFilterData.header }} - </h5> - <div class="gl-my-5"> + </div> + <div> <gl-label v-for="label in unappliedNewLabels" :key="label.key" diff --git a/app/assets/javascripts/search/sidebar/components/language_filter/index.vue b/app/assets/javascripts/search/sidebar/components/language_filter/index.vue index 4a9975641c6..d0c895530cd 100644 --- a/app/assets/javascripts/search/sidebar/components/language_filter/index.vue +++ b/app/assets/javascripts/search/sidebar/components/language_filter/index.vue @@ -75,9 +75,9 @@ export default { <template> <div v-if="hasBuckets" class="language-filter-checkbox"> - <h5 class="gl-mt-0 gl-mb-5 gl-font-sm"> + <div class="gl-mb-2 gl-font-weight-bold gl-font-sm"> {{ $options.languageFilterData.header }} - </h5> + </div> <div v-if="!aggregations.error" class="gl-overflow-x-hidden gl-overflow-y-auto" diff --git a/app/assets/javascripts/search/sidebar/components/radio_filter.vue b/app/assets/javascripts/search/sidebar/components/radio_filter.vue index d67844b93a7..f961bfea608 100644 --- a/app/assets/javascripts/search/sidebar/components/radio_filter.vue +++ b/app/assets/javascripts/search/sidebar/components/radio_filter.vue @@ -57,9 +57,9 @@ export default { <template> <div> - <h5 class="gl-mt-0 gl-mb-5 gl-font-sm"> + <div class="gl-mb-2 gl-font-weight-bold gl-font-sm"> {{ filterData.header }} - </h5> + </div> <gl-form-radio-group v-model="selectedFilter"> <gl-form-radio v-for="f in filtersArray" :key="f.value" :value="f.value"> {{ radioLabel(f) }} diff --git a/app/assets/javascripts/vue_shared/mixins/timeago.js b/app/assets/javascripts/vue_shared/mixins/timeago.js index 61e45fa5195..438da925937 100644 --- a/app/assets/javascripts/vue_shared/mixins/timeago.js +++ b/app/assets/javascripts/vue_shared/mixins/timeago.js @@ -1,4 +1,4 @@ -import { formatDate, getTimeago, timeagoLanguageCode } from '~/lib/utils/datetime_utility'; +import { getTimeago, localeDateFormat, timeagoLanguageCode } from '~/lib/utils/datetime_utility'; /** * Mixin with time ago methods used in some vue components @@ -12,7 +12,7 @@ export default { }, tooltipTitle(time) { - return formatDate(time); + return localeDateFormat.asDateTimeFull.format(time); }, }, }; diff --git a/app/graphql/mutations/projects/star.rb b/app/graphql/mutations/projects/star.rb new file mode 100644 index 00000000000..e4b64235c9a --- /dev/null +++ b/app/graphql/mutations/projects/star.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Mutations + module Projects + class Star < BaseMutation + graphql_name 'StarProject' + + authorize :read_project + + argument :project_id, + ::Types::GlobalIDType[::Project], + required: true, + description: 'Full path of the project to star or unstar.' + + argument :starred, + GraphQL::Types::Boolean, + required: true, + description: 'Indicates whether to star or unstar the project.' + + field :count, + GraphQL::Types::String, + null: false, + description: 'Number of stars for the project.' + + def resolve(project_id:, starred:) + project = authorized_find!(id: project_id) + + if current_user.starred?(project) != starred + current_user.toggle_star(project) + project.reset + end + + { + count: project.star_count + } + end + end + end +end diff --git a/app/graphql/types/current_user_type.rb b/app/graphql/types/current_user_type.rb new file mode 100644 index 00000000000..d5ecdeba9e2 --- /dev/null +++ b/app/graphql/types/current_user_type.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Types + # rubocop:disable Graphql/AuthorizeTypes -- This is not necessary because the superclass declares the authorization + class CurrentUserType < ::Types::UserType + graphql_name 'CurrentUser' + description 'The currently authenticated GitLab user.' + end + # rubocop:enable Graphql/AuthorizeTypes +end + +::Types::CurrentUserType.prepend_mod diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index d0a9ea11a27..3a2dea92cdc 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -108,6 +108,7 @@ module Types mount_mutation Mutations::Notes::Destroy mount_mutation Mutations::Organizations::Create, alpha: { milestone: '16.6' } mount_mutation Mutations::Projects::SyncFork, calls_gitaly: true, alpha: { milestone: '15.9' } + mount_mutation Mutations::Projects::Star, alpha: { milestone: '16.7' } mount_mutation Mutations::Releases::Create mount_mutation Mutations::Releases::Update mount_mutation Mutations::Releases::Delete diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 6b7814754a4..9acff9eb2b1 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -48,7 +48,7 @@ module Types required: true, description: 'Global ID of the container repository.' end - field :current_user, Types::UserType, + field :current_user, Types::CurrentUserType, null: true, description: "Get information about current user." field :design_management, Types::DesignManagementType, diff --git a/app/graphql/types/user_interface.rb b/app/graphql/types/user_interface.rb index 040711b5f58..2a628bb3ceb 100644 --- a/app/graphql/types/user_interface.rb +++ b/app/graphql/types/user_interface.rb @@ -229,5 +229,3 @@ module Types end end end - -Types::UserInterface.prepend_mod diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb index 87ca5fddf14..c5910236d51 100644 --- a/app/graphql/types/user_type.rb +++ b/app/graphql/types/user_type.rb @@ -14,3 +14,5 @@ module Types present_using UserPresenter end end + +Types::UserType.prepend_mod diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 051568a5674..c8fc10e6c94 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -2172,15 +2172,7 @@ class MergeRequest < ApplicationRecord end def current_patch_id_sha - return merge_request_diff.patch_id_sha if merge_request_diff.patch_id_sha.present? - - base_sha = diff_refs&.base_sha - head_sha = diff_refs&.head_sha - - return unless base_sha && head_sha - return if base_sha == head_sha - - project.repository.get_patch_id(base_sha, head_sha) + merge_request_diff.get_patch_id_sha end private diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index 4c87b78d9f2..000fb9117af 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -237,6 +237,17 @@ class MergeRequestDiff < ApplicationRecord ) end + def get_patch_id_sha + return patch_id_sha if patch_id_sha.present? + + set_patch_id_sha + + return unless patch_id_sha.present? + + save + patch_id_sha + end + def set_as_latest_diff # Don't set merge_head diff as latest so it won't get considered as the # MergeRequest#merge_request_diff. |