Skip to content

Commit

Permalink
Add opt-out option for email notifications in user settings (gitroomh…
Browse files Browse the repository at this point in the history
  • Loading branch information
arafat4693 committed May 25, 2024
1 parent 3d0cc13 commit 3e80092
Show file tree
Hide file tree
Showing 8 changed files with 315 additions and 200 deletions.
50 changes: 39 additions & 11 deletions apps/backend/src/api/routes/settings.controller.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common';
import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request';
import { Organization } from '@prisma/client';
import { Organization, User } from '@prisma/client';
import { StarsService } from '@gitroom/nestjs-libraries/database/prisma/stars/stars.service';
import { CheckPolicies } from '@gitroom/backend/services/auth/permissions/permissions.ability';
import {
AuthorizationActions,
Sections,
} from '@gitroom/backend/services/auth/permissions/permissions.service';
import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service';
import {AddTeamMemberDto} from "@gitroom/nestjs-libraries/dtos/settings/add.team.member.dto";
import {ApiTags} from "@nestjs/swagger";
import { AddTeamMemberDto } from '@gitroom/nestjs-libraries/dtos/settings/add.team.member.dto';
import { ApiTags } from '@nestjs/swagger';
import { UsersService } from '@gitroom/nestjs-libraries/database/prisma/users/users.service';
import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request';

@ApiTags('Settings')
@Controller('/settings')
export class SettingsController {
constructor(
private _starsService: StarsService,
private _organizationService: OrganizationService
private _organizationService: OrganizationService,
private _usersService: UsersService
) {}

@Get('/github')
Expand Down Expand Up @@ -105,26 +108,51 @@ export class SettingsController {
}

@Get('/team')
@CheckPolicies([AuthorizationActions.Create, Sections.TEAM_MEMBERS], [AuthorizationActions.Create, Sections.ADMIN])
@CheckPolicies(
[AuthorizationActions.Create, Sections.TEAM_MEMBERS],
[AuthorizationActions.Create, Sections.ADMIN]
)
async getTeam(@GetOrgFromRequest() org: Organization) {
return this._organizationService.getTeam(org.id);
}

@Post('/team')
@CheckPolicies([AuthorizationActions.Create, Sections.TEAM_MEMBERS], [AuthorizationActions.Create, Sections.ADMIN])
@CheckPolicies(
[AuthorizationActions.Create, Sections.TEAM_MEMBERS],
[AuthorizationActions.Create, Sections.ADMIN]
)
async inviteTeamMember(
@GetOrgFromRequest() org: Organization,
@Body() body: AddTeamMemberDto,
@GetOrgFromRequest() org: Organization,
@Body() body: AddTeamMemberDto
) {
return this._organizationService.inviteTeamMember(org.id, body);
}

@Delete('/team/:id')
@CheckPolicies([AuthorizationActions.Create, Sections.TEAM_MEMBERS], [AuthorizationActions.Create, Sections.ADMIN])
@CheckPolicies(
[AuthorizationActions.Create, Sections.TEAM_MEMBERS],
[AuthorizationActions.Create, Sections.ADMIN]
)
deleteTeamMember(
@GetOrgFromRequest() org: Organization,
@Param('id') id: string
@GetOrgFromRequest() org: Organization,
@Param('id') id: string
) {
return this._organizationService.deleteTeamMember(org, id);
}

@Get('/email-notifications/:userId')
async getEmailNotifications(@Param('userId') userId: string) {
return this._usersService.getEmailNotifications(userId);
}

@Post('/email-notifications/:userId')
async updateEmailNotifications(
@Param('userId') userId: string,
@Body('emailNotifications') emailNotifications: boolean
) {
return await this._usersService.updateEmailNotifications(
userId,
emailNotifications
);
}
}
9 changes: 8 additions & 1 deletion apps/backend/src/api/routes/users.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,18 @@ export class UsersController {
throw new HttpException('Organization not found', 401);
}

const isEmailNotification = await this._userService.getEmailNotifications(
user.id
);

return {
...user,
emailNotifications: isEmailNotification.emailNotifications,
orgId: organization.id,
// @ts-ignore
totalChannels: organization?.subscription?.totalChannels || pricing.FREE.channel,
totalChannels:
// @ts-ignore
organization?.subscription?.totalChannels || pricing.FREE.channel,
// @ts-ignore
tier: organization?.subscription?.subscriptionTier || 'FREE',
// @ts-ignore
Expand Down
51 changes: 44 additions & 7 deletions apps/frontend/src/components/settings/settings.component.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
'use client';

import { GithubComponent } from '@gitroom/frontend/components/settings/github.component';
import { useCallback, useEffect } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { useUser } from '@gitroom/frontend/components/layout/user.context';
import { TeamsComponent } from '@gitroom/frontend/components/settings/teams.component';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import useSWR from 'swr';
import { useSWRConfig } from 'swr';
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
import { useRouter } from 'next/navigation';
import { isGeneral } from '@gitroom/react/helpers/is.general';
import { Checkbox } from '@gitroom/react/form/checkbox';

const general = isGeneral();

export const SettingsComponent = () => {
const user = useUser();
const router = useRouter();
const { mutate } = useSWRConfig();
const [isChecked, setIsChecked] = useState(false);

const fetch = useFetch();

Expand All @@ -32,6 +36,34 @@ export const SettingsComponent = () => {
return { github, organizations };
}, []);

const toggleEmailNotification = useCallback(async () => {
if (!user || Object.keys(user).length === 0) return;

const newSetting = !isChecked;
setIsChecked(newSetting);

await fetch(`/settings/email-notifications/${user.id}`, {
method: 'POST',
body: JSON.stringify({ emailNotifications: newSetting }),
});

mutate(
'/user/self',
{
...user,
emailNotifications: newSetting,
},
{
revalidate: false,
}
);
}, [user, fetch, mutate, isChecked, setIsChecked]);

useEffect(() => {
if (!user || Object.keys(user).length === 0) return;
setIsChecked(user.emailNotifications);
}, [user, setIsChecked]);

const { isLoading: isLoadingSettings, data: loadAll } = useSWR(
'load-all',
load
Expand Down Expand Up @@ -63,12 +95,17 @@ export const SettingsComponent = () => {
github={loadAll.github}
organizations={loadAll.organizations}
/>
{/*<div className="flex gap-[5px]">*/}
{/* <div>*/}
{/* <Checkbox disableForm={true} checked={true} name="Send Email" />*/}
{/* </div>*/}
{/* <div>Show news with everybody in Gitroom</div>*/}
{/*</div>*/}
<div className="flex gap-[5px]">
<div>
<Checkbox
disableForm={true}
checked={isChecked}
name="Send Email"
onChange={toggleEmailNotification}
/>
</div>
<div>Show news with everybody in Gitroom</div>
</div>
</div>
)}
{!!user?.tier?.team_members && <TeamsComponent />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,23 @@ export class NotificationService {
);
}

async inAppNotification(orgId: string, subject: string, message: string, sendEmail = false) {
async inAppNotification(
orgId: string,
subject: string,
message: string,
sendEmail = false
) {
await this._notificationRepository.createNotification(orgId, message);
if (!sendEmail) {
return;
}

const userOrg = await this._organizationRepository.getAllUsersOrgs(orgId);
for (const user of userOrg?.users || []) {
await this.sendEmail(user.user.email, subject, message);
if (user.user.emailNotifications) {
// Check if user wants email notifications
await this.sendEmail(user.user.email, subject, message);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ export class OrganizationRepository {
select: {
email: true,
id: true,
emailNotifications: true,
},
},
},
Expand Down
Loading

0 comments on commit 3e80092

Please sign in to comment.