Skip to content

Commit

Permalink
fix: Inbox view Read/Snoozed display filters (#8907)
Browse files Browse the repository at this point in the history
* fix: Notification filters

* Update notification_finder.rb

* Update notification_finder.rb

* Update notification_finder.rb

* fix: spec

* fix: specs

* Update notification_finder.rb

* fix: add more fixes

* Update notification_finder.rb

* fix: specs

* chore: better comments

* chore: removed filtering

* chore: refactoring

* fix: review fixes

* fix: API call

* chore: Minor fix

* Rename spec

* Fix params getting undefined

* Fix finder

---------

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Pranav <pranav@chatwoot.com>
  • Loading branch information
4 people committed Feb 17, 2024
1 parent 6eb0637 commit cd06b2b
Show file tree
Hide file tree
Showing 10 changed files with 105 additions and 253 deletions.
Expand Up @@ -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

Expand Down
24 changes: 14 additions & 10 deletions app/finders/notification_finder.rb
Expand Up @@ -10,8 +10,8 @@ def initialize(current_user, current_account, params = {})
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
Expand All @@ -26,27 +26,31 @@ def count

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 fitler_read_notifications
@notifications = @notifications.where(read_at: nil) unless type_included?('read')
end

def filter_by_read_status
@notifications = @notifications.where.not(read_at: nil) if params[:type] == 'read'
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
5 changes: 3 additions & 2 deletions app/javascript/dashboard/api/notifications.js
Expand Up @@ -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,
},
});
}
Expand Down
40 changes: 28 additions & 12 deletions app/javascript/dashboard/api/specs/notifications.spec.js
Expand Up @@ -27,20 +27,36 @@ describe('#NotificationAPI', () => {
window.axios = originalAxios;
});

it('#get', () => {
notificationsAPI.get({
page: 1,
status: 'read',
type: 'Conversation',
sortOrder: 'desc',
describe('#get', () => {
it('generates the API call if both params are available', () => {
notificationsAPI.get({
page: 1,
status: 'snoozed',
type: 'read',
sortOrder: 'desc',
});
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/notifications', {
params: {
page: 1,
sort_order: 'desc',
includes: ['snoozed', 'read'],
},
});
});
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/notifications', {
params: {

it('generates the API call if one of the params are available', () => {
notificationsAPI.get({
page: 1,
status: 'read',
type: 'Conversation',
sort_order: 'desc',
},
type: 'read',
sortOrder: 'desc',
});
expect(axiosMock.get).toHaveBeenCalledWith('/api/v1/notifications', {
params: {
page: 1,
sort_order: 'desc',
includes: ['read'],
},
});
});
});

Expand Down
@@ -1,16 +1,13 @@
import { applyInboxPageFilters, sortComparator } from './helpers';
import { sortComparator } from './helpers';

export const getters = {
getNotifications($state) {
return Object.values($state.records).sort((n1, n2) => n2.id - n1.id);
},
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;
},
Expand Down
28 changes: 0 additions & 28 deletions 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',
Expand Down
Expand Up @@ -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',
Expand Down
@@ -1,10 +1,4 @@
import {
filterByStatus,
filterByType,
filterByTypeAndStatus,
applyInboxPageFilters,
sortComparator,
} from '../../notifications/helpers';
import { sortComparator } from '../../notifications/helpers';

const notifications = [
{
Expand Down Expand Up @@ -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';
Expand Down
10 changes: 10 additions & 0 deletions spec/factories/notifications.rb
Expand Up @@ -6,5 +6,15 @@
notification_type { 'conversation_assignment' }
user
account
read_at { nil }
snoozed_until { nil }
end

trait :read do
read_at { DateTime.now.utc - 3.days }
end

trait :snoozed do
snoozed_until { DateTime.now.utc + 3.days }
end
end

0 comments on commit cd06b2b

Please sign in to comment.