diff --git a/app/controllers/api/v1/accounts/notifications_controller.rb b/app/controllers/api/v1/accounts/notifications_controller.rb index 0eeff5695..3ac0568e3 100644 --- a/app/controllers/api/v1/accounts/notifications_controller.rb +++ b/app/controllers/api/v1/accounts/notifications_controller.rb @@ -7,8 +7,8 @@ class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseContro before_action :set_current_page, only: [:index] def index + @notifications = notification_finder.notifications @unread_count = notification_finder.unread_count - @notifications = notification_finder.perform @count = notification_finder.count end diff --git a/app/finders/notification_finder.rb b/app/finders/notification_finder.rb index 7e5789372..ccfe470a0 100644 --- a/app/finders/notification_finder.rb +++ b/app/finders/notification_finder.rb @@ -10,8 +10,8 @@ class NotificationFinder set_up end - def perform - notifications + def notifications + @notifications.page(current_page).per(RESULTS_PER_PAGE).order(last_activity_at: sort_order) end def unread_count @@ -26,27 +26,31 @@ class NotificationFinder def set_up find_all_notifications - filter_by_read_status - filter_by_status + filter_snoozed_notifications + fitler_read_notifications end def find_all_notifications @notifications = current_user.notifications.where(account_id: @current_account.id) end - def filter_by_status - @notifications = @notifications.where('snoozed_until > ?', DateTime.now.utc) if params[:status] == 'snoozed' + def filter_snoozed_notifications + @notifications = @notifications.where(snoozed_until: nil) unless type_included?('snoozed') end - def filter_by_read_status - @notifications = @notifications.where.not(read_at: nil) if params[:type] == 'read' + def fitler_read_notifications + @notifications = @notifications.where(read_at: nil) unless type_included?('read') + end + + def type_included?(type) + (params[:includes] || []).include?(type) end def current_page params[:page] || 1 end - def notifications - @notifications.page(current_page).per(RESULTS_PER_PAGE).order(last_activity_at: params[:sort_order] || :desc) + def sort_order + params[:sort_order] || :desc end end diff --git a/app/javascript/dashboard/api/notifications.js b/app/javascript/dashboard/api/notifications.js index 65fc937b7..65284623f 100644 --- a/app/javascript/dashboard/api/notifications.js +++ b/app/javascript/dashboard/api/notifications.js @@ -7,12 +7,13 @@ class NotificationsAPI extends ApiClient { } get({ page, status, type, sortOrder }) { + const includesFilter = [status, type].filter(value => !!value); + return axios.get(this.url, { params: { page, - status, - type, sort_order: sortOrder, + includes: includesFilter, }, }); } diff --git a/app/javascript/dashboard/api/specs/notifications.spec.js b/app/javascript/dashboard/api/specs/notifications.spec.js index bc06eaa2b..fe748fe19 100644 --- a/app/javascript/dashboard/api/specs/notifications.spec.js +++ b/app/javascript/dashboard/api/specs/notifications.spec.js @@ -27,20 +27,36 @@ describe('#NotificationAPI', () => { window.axios = originalAxios; }); - it('#get', () => { - notificationsAPI.get({ - page: 1, - status: 'read', - type: 'Conversation', - sortOrder: 'desc', - }); - expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/notifications', { - params: { + describe('#get', () => { + it('generates the API call if both params are available', () => { + notificationsAPI.get({ page: 1, - status: 'read', - type: 'Conversation', - sort_order: 'desc', - }, + status: 'snoozed', + type: 'read', + sortOrder: 'desc', + }); + expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/notifications', { + params: { + page: 1, + sort_order: 'desc', + includes: ['snoozed', 'read'], + }, + }); + }); + + it('generates the API call if one of the params are available', () => { + notificationsAPI.get({ + page: 1, + type: 'read', + sortOrder: 'desc', + }); + expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/notifications', { + params: { + page: 1, + sort_order: 'desc', + includes: ['read'], + }, + }); }); }); diff --git a/app/javascript/dashboard/store/modules/notifications/getters.js b/app/javascript/dashboard/store/modules/notifications/getters.js index ece9e228d..b5d0787e1 100644 --- a/app/javascript/dashboard/store/modules/notifications/getters.js +++ b/app/javascript/dashboard/store/modules/notifications/getters.js @@ -1,4 +1,4 @@ -import { applyInboxPageFilters, sortComparator } from './helpers'; +import { sortComparator } from './helpers'; export const getters = { getNotifications($state) { @@ -6,11 +6,8 @@ export const getters = { }, getFilteredNotifications: $state => filters => { const sortOrder = filters.sortOrder === 'desc' ? 'newest' : 'oldest'; - const filteredNotifications = Object.values($state.records).filter( - notification => applyInboxPageFilters(notification, filters) - ); - const sortedNotifications = filteredNotifications.sort((a, b) => - sortComparator(a, b, sortOrder) + const sortedNotifications = Object.values($state.records).sort((n1, n2) => + sortComparator(n1, n2, sortOrder) ); return sortedNotifications; }, diff --git a/app/javascript/dashboard/store/modules/notifications/helpers.js b/app/javascript/dashboard/store/modules/notifications/helpers.js index 53c05c94e..33b864c43 100644 --- a/app/javascript/dashboard/store/modules/notifications/helpers.js +++ b/app/javascript/dashboard/store/modules/notifications/helpers.js @@ -1,31 +1,3 @@ -export const filterByStatus = (snoozedUntil, filterStatus) => - filterStatus === 'snoozed' ? !!snoozedUntil : !snoozedUntil; - -export const filterByType = (readAt, filterType) => - filterType === 'read' ? !!readAt : !readAt; - -export const filterByTypeAndStatus = ( - readAt, - snoozedUntil, - filterType, - filterStatus -) => { - const shouldFilterByStatus = filterByStatus(snoozedUntil, filterStatus); - const shouldFilterByType = filterByType(readAt, filterType); - return shouldFilterByStatus && shouldFilterByType; -}; - -export const applyInboxPageFilters = (notification, filters) => { - const { status, type } = filters; - const { read_at: readAt, snoozed_until: snoozedUntil } = notification; - - if (status && type) - return filterByTypeAndStatus(readAt, snoozedUntil, type, status); - if (status && !type) return filterByStatus(snoozedUntil, status); - if (!status && type) return filterByType(readAt, type); - return true; -}; - const INBOX_SORT_OPTIONS = { newest: 'desc', oldest: 'asc', diff --git a/app/javascript/dashboard/store/modules/specs/notifications/getters.spec.js b/app/javascript/dashboard/store/modules/specs/notifications/getters.spec.js index 5418f5b66..60b648faf 100644 --- a/app/javascript/dashboard/store/modules/specs/notifications/getters.spec.js +++ b/app/javascript/dashboard/store/modules/specs/notifications/getters.spec.js @@ -34,6 +34,8 @@ describe('#getters', () => { sortOrder: 'desc', }; expect(getters.getFilteredNotifications(state)(filters)).toEqual([ + { id: 1, read_at: '2024-02-07T11:42:39.988Z', snoozed_until: null }, + { id: 2, read_at: null, snoozed_until: null }, { id: 3, read_at: '2024-02-07T11:42:39.988Z', diff --git a/app/javascript/dashboard/store/modules/specs/notifications/helpers.spec.js b/app/javascript/dashboard/store/modules/specs/notifications/helpers.spec.js index f5a9a3d9b..6d0338032 100644 --- a/app/javascript/dashboard/store/modules/specs/notifications/helpers.spec.js +++ b/app/javascript/dashboard/store/modules/specs/notifications/helpers.spec.js @@ -1,10 +1,4 @@ -import { - filterByStatus, - filterByType, - filterByTypeAndStatus, - applyInboxPageFilters, - sortComparator, -} from '../../notifications/helpers'; +import { sortComparator } from '../../notifications/helpers'; const notifications = [ { @@ -45,126 +39,6 @@ const notifications = [ }, ]; -describe('#filterByStatus', () => { - it('returns the notifications with snoozed status', () => { - const filters = { status: 'snoozed' }; - notifications.forEach(notification => { - expect( - filterByStatus(notification.snoozed_until, filters.status) - ).toEqual(notification.snoozed_until !== null); - }); - }); - it('returns true if the notification is snoozed', () => { - const filters = { status: 'snoozed' }; - expect( - filterByStatus(notifications[3].snoozed_until, filters.status) - ).toEqual(true); - }); - it('returns false if the notification is not snoozed', () => { - const filters = { status: 'snoozed' }; - expect( - filterByStatus(notifications[2].snoozed_until, filters.status) - ).toEqual(false); - }); -}); - -describe('#filterByType', () => { - it('returns the notifications with read status', () => { - const filters = { type: 'read' }; - notifications.forEach(notification => { - expect(filterByType(notification.read_at, filters.type)).toEqual( - notification.read_at !== null - ); - }); - }); - it('returns true if the notification is read', () => { - const filters = { type: 'read' }; - expect(filterByType(notifications[0].read_at, filters.type)).toEqual(true); - }); - it('returns false if the notification is not read', () => { - const filters = { type: 'read' }; - expect(filterByType(notifications[1].read_at, filters.type)).toEqual(false); - }); -}); - -describe('#filterByTypeAndStatus', () => { - it('returns the notifications with type and status', () => { - const filters = { type: 'read', status: 'snoozed' }; - notifications.forEach(notification => { - expect( - filterByTypeAndStatus( - notification.read_at, - notification.snoozed_until, - filters.type, - filters.status - ) - ).toEqual( - notification.read_at !== null && notification.snoozed_until !== null - ); - }); - }); - it('returns true if the notification is read and snoozed', () => { - const filters = { type: 'read', status: 'snoozed' }; - expect( - filterByTypeAndStatus( - notifications[4].read_at, - notifications[4].snoozed_until, - filters.type, - filters.status - ) - ).toEqual(true); - }); - it('returns false if the notification is not read and snoozed', () => { - const filters = { type: 'read', status: 'snoozed' }; - expect( - filterByTypeAndStatus( - notifications[3].read_at, - notifications[3].snoozed_until, - filters.type, - filters.status - ) - ).toEqual(false); - }); -}); - -describe('#applyInboxPageFilters', () => { - it('returns the notifications with type and status', () => { - const filters = { type: 'read', status: 'snoozed' }; - notifications.forEach(notification => { - expect(applyInboxPageFilters(notification, filters)).toEqual( - filterByTypeAndStatus( - notification.read_at, - notification.snoozed_until, - filters.type, - filters.status - ) - ); - }); - }); - it('returns the notifications with type only', () => { - const filters = { type: 'read', status: null }; - notifications.forEach(notification => { - expect(applyInboxPageFilters(notification, filters)).toEqual( - filterByType(notification.read_at, filters.type) - ); - }); - }); - it('returns the notifications with status only', () => { - const filters = { type: null, status: 'snoozed' }; - notifications.forEach(notification => { - expect(applyInboxPageFilters(notification, filters)).toEqual( - filterByStatus(notification.snoozed_until, filters.status) - ); - }); - }); - it('returns true if there are no filters', () => { - const filters = { type: null, status: null }; - notifications.forEach(notification => { - expect(applyInboxPageFilters(notification, filters)).toEqual(true); - }); - }); -}); - describe('#sortComparator', () => { it('returns the notifications sorted by newest', () => { const sortOrder = 'newest'; diff --git a/app/javascript/v3/components/Form/Textarea.vue b/app/javascript/v3/components/Form/Textarea.vue new file mode 100644 index 000000000..e43c411f5 --- /dev/null +++ b/app/javascript/v3/components/Form/Textarea.vue @@ -0,0 +1,83 @@ +