Skip to content
This repository has been archived by the owner on Mar 30, 2021. It is now read-only.

Commit

Permalink
Merge pull request #43 from pascal-zarrad/feature/notifications
Browse files Browse the repository at this point in the history
Implement basic notification system
  • Loading branch information
pascal-zarrad authored Jul 11, 2020
2 parents 337be4a + a00ccc2 commit 49d586d
Show file tree
Hide file tree
Showing 18 changed files with 550 additions and 11 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ docker-compose run node yarn test:unit
docker-compose run node yarn lint
```

### Show build log
```
docker-compose logs -f node
```

## Contributing

You are welcome to contribute to SoftDocLinker if you have improvements or bug fixes.
Expand Down
9 changes: 8 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<template>
<v-app id="softdoclinker-app">
<!-- Notification management -->
<notifications-component />

<!-- Drawer for documentation view -->
<v-navigation-drawer
:clipped="$vuetify.breakpoint.lgAndUp"
v-model="drawer"
Expand All @@ -10,6 +14,7 @@

<navigation-component :drawer="drawer" @toggleDrawer="toggleDrawer" />

<!-- Content area for loaded documentation -->
<v-content>
<v-container class="fill-height" fluid>
<v-row align="center" justify="center" />
Expand All @@ -22,6 +27,7 @@
<script lang="ts">
import NavigationComponent from "@/components/NavigationComponent.vue";
import RefreshDataComponent from "@/components/RefreshDataComponent.vue";
import NotificationsComponent from "@/components/NotificationsComponent.vue";
import defaultSharedState from "@/model/defaultSharedState";
import SharedStateInterface from "@/model/SharedStateInterface";
import StateManagementInterface from "@/model/StateManagementInterface";
Expand All @@ -31,7 +37,8 @@ export default Vue.extend({
name: "App",
components: {
NavigationComponent,
RefreshDataComponent
RefreshDataComponent,
NotificationsComponent
},
data: function() {
return {
Expand Down
25 changes: 24 additions & 1 deletion src/SoftDocLinker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import DocCollectionInterface from "@/model/doc/DocCollectionInterface";
import StateManagementFactory from "@/model/StateManagementFactory";
import StateManagementInterface from "@/model/StateManagementInterface";
import SoftDocLinkerInterface from "@/SoftDocLinkerInterface";
import NotificationManagementInterface from "@/service/notification/NotificationManagementInterface";
import NotificationManagementFactory from "@/service/notification/NotificationManagementFactory";
import Vue from "vue";

/**
Expand Down Expand Up @@ -77,6 +79,11 @@ export class SoftDocLinker implements SoftDocLinkerInterface {
*/
protected _stateManagement?: StateManagementInterface;

/**
* Manager for notifications that are currently displayed
*/
protected _notificationManagement: NotificationManagementInterface;

/**
* Constructor
*
Expand All @@ -93,7 +100,8 @@ export class SoftDocLinker implements SoftDocLinkerInterface {
cacheManagementFactory: CacheManagementFactory = new CacheManagementFactory(),
docCollectionDataProviderFactory: DocCollectionDataProviderFactory = new DocCollectionDataProviderFactory(),
docCollectionDataRepositoryFactory: DocCollectionDataRepositoryFactory = new DocCollectionDataRepositoryFactory(),
stateManagementFactory: StateManagementFactory = new StateManagementFactory()
stateManagementFactory: StateManagementFactory = new StateManagementFactory(),
notificationManagement: NotificationManagementInterface = new NotificationManagementFactory().create()
) {
this._configDataProviderFactory = configDataProviderFactory;
this._configDataRepositoryFactory = configDataRepositoryFactory;
Expand All @@ -102,6 +110,7 @@ export class SoftDocLinker implements SoftDocLinkerInterface {
this._docCollectionDataProviderFactory = docCollectionDataProviderFactory;
this._docCollectionDataRepositoryFactory = docCollectionDataRepositoryFactory;
this._stateManagementFactory = stateManagementFactory;
this._notificationManagement = notificationManagement;
}

/**
Expand Down Expand Up @@ -161,9 +170,23 @@ export class SoftDocLinker implements SoftDocLinkerInterface {
this._stateManagement = this._stateManagementFactory.create(
await this.getConfigDataRepository(),
await this.getDocCollectionDataRepository(),
this.notificationManagement,
Vue.prototype.$sharedState
);

return this._stateManagement;
}

/**
* Getter: _notificationManagement.
*
* Get the notification management instance used to manage the
* the current notifications.
*
* @inheritdoc
*/
/* istanbul ignore next */
public get notificationManagement(): NotificationManagementInterface {
return this._notificationManagement;
}
}
9 changes: 9 additions & 0 deletions src/SoftDocLinkerInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import ConfigDataInterface from "@/model/config/ConfigDataInterface";
import DataRepositoryInterface from "@/model/DataRepositoryInterface";
import DocCollectionInterface from "@/model/doc/DocCollectionInterface";
import StateManagementInterface from "@/model/StateManagementInterface";
import NotificationManagementInterface from "@/service/notification/NotificationManagementInterface";

/**
* Defines the interface between our Vue components and
Expand Down Expand Up @@ -37,4 +38,12 @@ export default interface SoftDocLinkerInterface {
* the sharing of the applications state.
*/
getStateManagement(): Promise<StateManagementInterface>;

/**
* Get the notification management that is currently used by SoftDocLinker to
* handle the shown notifications.
*
* Should be implemented using a get accessor in implementations.
*/
readonly notificationManagement: NotificationManagementInterface;
}
58 changes: 58 additions & 0 deletions src/components/NotificationsComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<template>
<div>
<v-snackbar
v-for="notification in notifications"
:key="notification.id"
:bottom="true"
v-model="notification.show"
>
{{ notification.type }}: {{ notification.message }}
<v-btn
color="blue"
text
@click="submitCloseNotification(notification)"
>
Close
</v-btn>
</v-snackbar>
</div>
</template>

<script lang="ts">
import NotificationInterface from "@/model/notification/NotificationInterface";
import { Component, Vue } from "vue-property-decorator";
/**
* Component that handles notification display by making use of the
* Vuetify Snackbar component
*
* @since 2.0.0
*/
@Component({
name: "NotificationsComponent"
})
export default class NotificationsComponent extends Vue {
/**
* A computed property that provides the notifications array
* that provides all notifications that should be created by this component.
*
* @returns The (reactive) array that contains all visible notifications
*/
get notifications(): NotificationInterface[] {
return this.$softDocLinker.notificationManagement.notifications;
}
/**
* Called when a notification is being closed manually by clicking
* on the close button.
*
* @param notification The notification of which a close was triggered
*/
private submitCloseNotification(notification: NotificationInterface): void {
notification.show = false;
this.$softDocLinker.notificationManagement.removeNotification(
notification
);
}
}
</script>
2 changes: 1 addition & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import defaultSharedState from "./model/defaultSharedState";

// Set instance of data layer management with initial default values
Vue.prototype.$softDocLinker = new SoftDocLinker();
// Inilize shared state with default value
// Initialize shared state with default value
Vue.prototype.$sharedState = Vue.observable(defaultSharedState());

new Vue({
Expand Down
2 changes: 1 addition & 1 deletion src/model/AbstractDataRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import DataProviderInterface from "@/dataprovider/DataProviderInterface";
import DataRepositoryInterface from "@/model/DataRepositoryInterface";

/**
* Abstract data repository that can be implemented get
* Abstract data repository that can be implemented to get
* data from a DataProviderInterface while using caching
* mechanisms to improve performance.
*
Expand Down
30 changes: 26 additions & 4 deletions src/model/StateManagement.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import ConfigDataInterface from "@/model/config/ConfigDataInterface";
import ConfigDataRepository from "@/model/config/ConfigDataRepository";
import DataRepositoryInterface from "@/model/DataRepositoryInterface";
import defaultSharedState from "@/model/defaultSharedState";
import DocCollectionDataRepository from "@/model/doc/DocCollectionDataRepository";
import DocCollectionInterface from "@/model/doc/DocCollectionInterface";
import NotificationFactory from "@/model/notification/NotificationFactory";
import NotificationType from "@/model/notification/NotificationType";
import SharedStateInterface from "@/model/SharedStateInterface";
import StateManagementInterface from "@/model/StateManagementInterface";
import DocCollectionInterface from "@/model/doc/DocCollectionInterface";
import ConfigDataRepository from "@/model/config/ConfigDataRepository";
import DocCollectionDataRepository from "@/model/doc/DocCollectionDataRepository";
import NotificationManagementInterface from "@/service/notification/NotificationManagementInterface";

/**
* State management that manages the data used by the rendering.
Expand All @@ -27,6 +30,16 @@ export default class StateManagement implements StateManagementInterface {
DocCollectionInterface
>;

/**
* The notification management used to display notifications to the user
*/
protected _notificationManagement: NotificationManagementInterface;

/**
* Factory used to build notifications.
*/
protected _notificationFactory: NotificationFactory;

/**
* The shared state of SoftDocLinker
*/
Expand All @@ -42,10 +55,14 @@ export default class StateManagement implements StateManagementInterface {
constructor(
configDataRepository: DataRepositoryInterface<ConfigDataInterface>,
docDataRepository: DataRepositoryInterface<DocCollectionInterface>,
notificationManagement: NotificationManagementInterface,
notificationFactory: NotificationFactory = new NotificationFactory(),
sharedState: SharedStateInterface = defaultSharedState()
) {
this._configDataRepository = configDataRepository;
this._docDataRepository = docDataRepository;
this._notificationManagement = notificationManagement;
this._notificationFactory = notificationFactory;
this._sharedState = sharedState;
}

Expand All @@ -66,7 +83,12 @@ export default class StateManagement implements StateManagementInterface {
forceRefresh
);
} catch (error) {
throw error;
this._notificationManagement.notify(
this._notificationFactory.create(
"Failed to refresh data!",
NotificationType.ERROR
)
);
} finally {
this._sharedState.loading = false;
}
Expand Down
5 changes: 5 additions & 0 deletions src/model/StateManagementFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,23 @@ import StateManagement from "@/model/StateManagement";
import StateManagementInterface from "@/model/StateManagementInterface";
import SharedStateInterface from "@/model/SharedStateInterface";
import defaultSharedState from "@/model/defaultSharedState";
import NotificationManagementInterface from "@/service/notification/NotificationManagementInterface";
import NotificationFactory from "./notification/NotificationFactory";

export default class StateManagementFactory {
public create(
configDataRepository: DataRepositoryInterface<ConfigDataInterface>,
docCollectionRepository: DataRepositoryInterface<
DocCollectionInterface
>,
notificationManagement: NotificationManagementInterface,
sharedState: SharedStateInterface = defaultSharedState()
): StateManagementInterface {
return new StateManagement(
configDataRepository,
docCollectionRepository,
notificationManagement,
new NotificationFactory(),
sharedState
);
}
Expand Down
25 changes: 25 additions & 0 deletions src/model/notification/NotificationFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* istanbul ignore file */

import NotificationInterface from "./NotificationInterface";

/**
* Factory to create notifications.
*
* @since 2.0.0
*/
export default class NotificationFactory {
/**
* Create a new notification.
*
* @param type The type of the notification to create
* @param message The message of the notification
* @returns A new notification with the given data
*/
public create(type: string, message: string): NotificationInterface {
return {
type: type,
message: message,
show: false
};
}
}
25 changes: 25 additions & 0 deletions src/model/notification/NotificationInterface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* istanbul ignore file */

/**
* Model of the data required to display a notification.
*
* @since 2.0.0
*/
export default interface NotificationInterface {
/**
* The message that is displayed by the notification.
*/
message: string;

/**
* The type the displayed notification should have
*
* @see ./NotificationType
*/
type: string;

/**
* State of this notification should be shown in the frontend.
*/
show: boolean;
}
24 changes: 24 additions & 0 deletions src/model/notification/NotificationType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* istanbul ignore file */

/**
* Notification type that provides different types for notifications
* to categorize them.
*
* @since 2.0.0
*/
export default class NotificationType {
/**
* Notification level: INFO
*/
public static readonly INFO = "INFO";

/**
* Notification level: SUCCESS
*/
public static readonly SUCCESS = "SUCCESS";

/**
* Notification level: ERROR
*/
public static readonly ERROR = "ERROR";
}
Loading

0 comments on commit 49d586d

Please sign in to comment.