Pull request #57: Notifications
Merge in RED/ui from notifications to master * commit '836354d89acf3f904901751d531b0b3212f7adf1': Dummy data notifications
This commit is contained in:
commit
3279e56f6b
@ -91,6 +91,7 @@ import { AceEditorModule } from 'ng2-ace-editor';
|
||||
import { TeamMembersComponent } from './components/team-members/team-members.component';
|
||||
import { AdminBreadcrumbsComponent } from './components/admin-page-header/admin-breadcrumbs.component';
|
||||
import { UserListingScreenComponent } from './screens/admin/users/user-listing-screen.component';
|
||||
import { NotificationsComponent } from './components/notifications/notifications.component';
|
||||
|
||||
export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
||||
@ -246,7 +247,8 @@ const matImports = [
|
||||
DictionaryOverviewScreenComponent,
|
||||
TeamMembersComponent,
|
||||
AdminBreadcrumbsComponent,
|
||||
UserListingScreenComponent
|
||||
UserListingScreenComponent,
|
||||
NotificationsComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
||||
@ -6,8 +6,10 @@
|
||||
[class.warn]="type === 'warn'"
|
||||
[disabled]="disabled"
|
||||
[class.small]="small"
|
||||
[class.overlay]="showDot"
|
||||
mat-icon-button
|
||||
>
|
||||
<mat-icon [svgIcon]="icon"></mat-icon>
|
||||
</button>
|
||||
<div class="dot" *ngIf="showDot"></div>
|
||||
</div>
|
||||
|
||||
@ -8,6 +8,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
export class CircleButtonComponent implements OnInit {
|
||||
@Input() icon: string;
|
||||
@Input() tooltip: string;
|
||||
@Input() showDot = false;
|
||||
@Input() tooltipPosition: 'above' | 'below' | 'before' | 'after' = 'above';
|
||||
@Input() tooltipClass: string;
|
||||
@Input() disabled = false;
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
<redaction-circle-button [matMenuTriggerFor]="overlay" icon="red:notification" [showDot]="hasUnread"></redaction-circle-button>
|
||||
<mat-menu #overlay="matMenu" class="notifications-menu" backdropClass="notifications-backdrop">
|
||||
<div *ngFor="let group of groupedNotifications | sortBy: 'desc':'dateString'">
|
||||
<div class="all-caps-label">{{ day(group) }}</div>
|
||||
<div
|
||||
class="notification"
|
||||
mat-menu-item
|
||||
*ngFor="let notification of group.notifications | sortBy: 'desc':'eventTime'"
|
||||
[class.unread]="!notification.read"
|
||||
>
|
||||
<redaction-initials-avatar></redaction-initials-avatar>
|
||||
<div class="notification-content">
|
||||
<div [innerHTML]="notification.message"></div>
|
||||
<div class="small-label mt-2">{{ eventTime(notification.eventTime) }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="dot"
|
||||
(click)="toggleRead(notification, $event)"
|
||||
[matTooltip]="(notification.read ? 'notifications.mark-unread' : 'notifications.mark-read') | translate"
|
||||
matTooltipPosition="before"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-menu>
|
||||
@ -0,0 +1,73 @@
|
||||
@import '../../../assets/styles/red-variables';
|
||||
|
||||
.mt-2 {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
::ng-deep .notifications-backdrop + .cdk-overlay-connected-position-bounding-box {
|
||||
right: 0 !important;
|
||||
|
||||
.notifications-menu {
|
||||
min-width: 400px !important;
|
||||
padding: 0 8px 16px;
|
||||
max-height: calc(100vh - 200px);
|
||||
|
||||
.all-caps-label {
|
||||
margin: 16px 0 6px 8px;
|
||||
}
|
||||
|
||||
.mat-menu-item.notification {
|
||||
padding: 8px 26px 10px 8px;
|
||||
margin: 2px 0 0 0;
|
||||
height: fit-content;
|
||||
position: relative;
|
||||
line-height: 18px;
|
||||
align-items: flex-start;
|
||||
|
||||
.notification-content {
|
||||
margin-top: 4px;
|
||||
margin-left: 8px;
|
||||
max-width: 320px;
|
||||
|
||||
> div {
|
||||
white-space: normal;
|
||||
|
||||
a {
|
||||
color: $accent;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dot {
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: $grey-4;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: $grey-6;
|
||||
|
||||
.dot {
|
||||
background-color: $grey-5;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
&.unread {
|
||||
background-color: rgba($primary, 0.1);
|
||||
|
||||
.dot {
|
||||
background-color: $primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import * as moment from 'moment';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
interface Notification {
|
||||
message: string;
|
||||
eventTime: number;
|
||||
read: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-notifications',
|
||||
templateUrl: './notifications.component.html',
|
||||
styleUrls: ['./notifications.component.scss']
|
||||
})
|
||||
export class NotificationsComponent implements OnInit {
|
||||
public notifications: Notification[] = [
|
||||
{ message: 'This is a notification with longer text wrapping on multiple lines', eventTime: 1607340971000, read: false },
|
||||
{ message: 'This is a <a>link</a>', eventTime: 1607254981000, read: true },
|
||||
{ message: 'This is a <b>notification 1</b>', eventTime: 1607254571000, read: false },
|
||||
{ message: 'Notification', eventTime: 1607385727000, read: true },
|
||||
{ message: 'Another notification', eventTime: 1606829412000, read: false }
|
||||
];
|
||||
public groupedNotifications: { dateString: string; notifications: Notification[] }[] = [];
|
||||
|
||||
constructor(private _translateService: TranslateService) {
|
||||
this._groupNotifications();
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
|
||||
public get hasUnread() {
|
||||
return this.notifications.filter((notification) => !notification.read).length > 0;
|
||||
}
|
||||
|
||||
private _groupNotifications() {
|
||||
const res = {};
|
||||
for (const notification of this.notifications) {
|
||||
const date = moment(notification.eventTime).format('YYYY-MM-DD');
|
||||
if (!res[date]) res[date] = [];
|
||||
res[date].push(notification);
|
||||
}
|
||||
for (const key of Object.keys(res)) {
|
||||
this.groupedNotifications.push({ dateString: key, notifications: res[key] });
|
||||
}
|
||||
}
|
||||
|
||||
public day(group: { dateString: string; notifications: Notification[] }): string {
|
||||
moment.locale(this._translateService.currentLang);
|
||||
return moment(group.notifications[0].eventTime).calendar({
|
||||
sameDay: `[${this._translateService.instant('notifications.today')}]`,
|
||||
lastDay: `[${this._translateService.instant('notifications.yesterday')}]`,
|
||||
nextDay: `[${this._translateService.instant('notifications.tomorrow')}]`,
|
||||
nextWeek: 'D MMMM',
|
||||
lastWeek: 'D MMMM',
|
||||
sameElse: 'D MMMM'
|
||||
});
|
||||
}
|
||||
|
||||
public eventTime(eventTime: number): string {
|
||||
moment.locale(this._translateService.currentLang);
|
||||
if (moment().isSame(eventTime, 'day')) {
|
||||
return moment(eventTime).fromNow();
|
||||
} else {
|
||||
return moment(eventTime).format('hh:mm A');
|
||||
}
|
||||
}
|
||||
|
||||
public toggleRead(notification: Notification, $event) {
|
||||
$event.stopPropagation();
|
||||
notification.read = !notification.read;
|
||||
}
|
||||
}
|
||||
@ -41,6 +41,7 @@ export class IconsModule {
|
||||
'logout',
|
||||
'menu',
|
||||
'needs-work',
|
||||
'notification',
|
||||
'pages',
|
||||
'plus',
|
||||
'preview',
|
||||
|
||||
@ -56,8 +56,9 @@
|
||||
<span class="dev-mode" *ngIf="userPreferenceService.areDevFeaturesEnabled" translate="dev-mode"></span>
|
||||
</div>
|
||||
<div class="menu right flex-2">
|
||||
<redaction-user-button [user]="user" [matMenuTriggerFor]="menu"></redaction-user-button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<redaction-notifications class="mr-8"></redaction-notifications>
|
||||
<redaction-user-button [user]="user" [matMenuTriggerFor]="userMenu"></redaction-user-button>
|
||||
<mat-menu #userMenu="matMenu">
|
||||
<button
|
||||
*ngIf="permissionsService.isManager()"
|
||||
(click)="appStateService.reset()"
|
||||
|
||||
@ -547,5 +547,12 @@
|
||||
"search": "Search..."
|
||||
},
|
||||
"dictionaries": "Dictionaries",
|
||||
"user-management": "User Management"
|
||||
"user-management": "User Management",
|
||||
"notifications": {
|
||||
"today": "Today",
|
||||
"yesterday": "Yesterday",
|
||||
"tomorrow": "Tomorrow",
|
||||
"mark-read": "Mark as read",
|
||||
"mark-unread": "Mark as unread"
|
||||
}
|
||||
}
|
||||
|
||||
17
apps/red-ui/src/assets/icons/general/notification.svg
Normal file
17
apps/red-ui/src/assets/icons/general/notification.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>AAFF7BC7-3583-4A2B-A91D-8A2F1A75D50E</title>
|
||||
<g id="Notifications" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="01.-Notifications-Dropdown" transform="translate(-1258.000000, -23.000000)">
|
||||
<rect x="0" y="0" width="1440" height="980"></rect>
|
||||
<g id="Header-Project-Details">
|
||||
<rect id="Rectangle" x="0" y="0" width="1440" height="60"></rect>
|
||||
<g id="Group-6" transform="translate(1248.000000, 13.000000)">
|
||||
<g id="status" transform="translate(10.000000, 10.000000)" fill="currentColor" fill-rule="nonzero">
|
||||
<path d="M12.6,10.5 L12.32,10.5 C11.55,8.89 11.2,7.07 11.2,5.25 L11.2,4.2 C11.2,1.89 9.31,0 7,0 C4.69,0 2.8,1.89 2.8,4.2 L2.8,5.25 C2.8,7.07 2.38,8.82 1.68,10.5 L1.4,10.5 C0.98,10.5 0.7,10.78 0.7,11.2 C0.7,11.62 0.98,11.9 1.4,11.9 L4.9,11.9 L4.9,11.9 C4.9,13.09 5.81,14 7,14 C8.19,14 9.1,13.09 9.1,11.9 L9.1,11.9 L12.6,11.9 C13.02,11.9 13.3,11.62 13.3,11.2 C13.3,10.78 13.02,10.5 12.6,10.5 Z M7.7,11.9 C7.7,12.32 7.42,12.6 7,12.6 C6.58,12.6 6.3,12.32 6.3,11.9 L6.3,11.9 L7.7,11.9 C7.7,11.9 7.7,11.9 7.7,11.9 Z M3.15,10.5 C3.85,8.82 4.2,7.07 4.2,5.25 L4.2,4.2 C4.2,2.66 5.46,1.4 7,1.4 C8.54,1.4 9.8,2.66 9.8,4.2 L9.8,5.25 C9.8,7.07 10.15,8.82 10.85,10.5 L3.15,10.5 Z" id="Shape"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@ -74,6 +74,7 @@ redaction-circle-button {
|
||||
}
|
||||
|
||||
redaction-chevron-button,
|
||||
redaction-circle-button,
|
||||
redaction-icon-button {
|
||||
&[aria-expanded='true'] {
|
||||
button {
|
||||
@ -81,11 +82,3 @@ redaction-icon-button {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redaction-circle-button {
|
||||
&[aria-expanded='true'] {
|
||||
button {
|
||||
background: $grey-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user