Skip to content

Commit

Permalink
Merge pull request #116 from getkirby/fiber/steps/notification
Browse files Browse the repository at this point in the history
Notification module
  • Loading branch information
distantnative authored Apr 5, 2023
2 parents b325a76 + 089bd74 commit 8314b63
Show file tree
Hide file tree
Showing 24 changed files with 654 additions and 146 deletions.
2 changes: 1 addition & 1 deletion panel/src/components/Dialogs/Dialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ export default {
// send a global success notification
if (success.message) {
this.$store.dispatch("notification/success", success.message);
this.$panel.notification.success(success.message);
}
// dispatch store actions that might have been defined in
Expand Down
2 changes: 1 addition & 1 deletion panel/src/components/Forms/Field/StructureField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ export default {
return true;
} catch (errors) {
this.$store.dispatch("notification/error", {
this.$panel.notification.error({
message: this.$t("error.form.incomplete"),
details: errors
});
Expand Down
6 changes: 3 additions & 3 deletions panel/src/components/Forms/FormButtons.vue
Original file line number Diff line number Diff line change
Expand Up @@ -234,19 +234,19 @@ export default {
try {
await this.$store.dispatch("content/save");
this.$events.$emit("model.update");
this.$store.dispatch("notification/success", ":)");
this.$panel.notification.success();
} catch (response) {
if (response.code === 403) {
return;
}
if (this.$helper.object.length(response.details) > 0) {
this.$store.dispatch("notification/error", {
this.$panel.notification.error({
message: this.$t("error.form.incomplete"),
details: response.details
});
} else {
this.$store.dispatch("notification/error", {
this.$panel.notification.error({
message: this.$t("error.form.notSaved"),
details: [
{
Expand Down
2 changes: 1 addition & 1 deletion panel/src/components/Forms/LoginCode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export default {
try {
await this.$api.auth.verifyCode(this.code);
this.$store.dispatch("notification/success", this.$t("welcome"));
this.$panel.notification.success(this.$t("welcome"));
if (this.mode === "password-reset") {
this.$go("reset-password");
Expand Down
5 changes: 4 additions & 1 deletion panel/src/components/Layout/Panel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
</template>

<!-- Fatal iframe -->
<k-fatal v-if="$store.state.fatal !== false" :html="$store.state.fatal" />
<k-fatal
v-if="$panel.notification.isFatal && $panel.notification.isOpen"
:html="$panel.notification.message"
/>

<!-- Offline warning -->
<k-offline-warning v-if="$system.isLocal === false" />
Expand Down
2 changes: 1 addition & 1 deletion panel/src/components/Misc/Fatal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<k-button
icon="cancel"
text="Close"
@click="$store.dispatch('fatal', false)"
@click="$panel.notification.close()"
/>
</template>
</k-bar>
Expand Down
28 changes: 18 additions & 10 deletions panel/src/components/Navigation/Topbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@
<div class="k-topbar-signals">
<!-- notifications -->
<k-button
v-if="notification"
v-if="notification && notification.type !== 'error'"
:icon="notification.icon"
:text="notification.message"
theme="positive"
:theme="notification.theme"
class="k-topbar-notification k-topbar-button"
@click="$store.dispatch('notification/close')"
@click="notification.close()"
/>

<!-- registration -->
Expand Down Expand Up @@ -70,13 +71,20 @@ export default {
},
computed: {
notification() {
if (
this.$store.state.notification.type &&
this.$store.state.notification.type !== "error"
) {
return this.$store.state.notification;
} else {
return null;
return this.$panel.notification.context === "view"
? this.$panel.notification
: null;
}
},
watch: {
notification(notification) {
// send the notification to the dialog instead
// of the topbar.
if (notification?.type === "error") {
this.$dialog({
component: "k-error-dialog",
props: notification.state()
});
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions panel/src/components/Sections/FilesSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -86,19 +86,19 @@ export default {
files: items.map((item) => item.id),
index: this.pagination.offset
});
this.$store.dispatch("notification/success", ":)");
this.$panel.notification.success();
this.$events.$emit("file.sort");
} catch (error) {
this.reload();
this.$store.dispatch("notification/error", error.message);
this.$panel.notification.error(error.message);
} finally {
this.isProcessing = false;
}
},
onUpload() {
this.$events.$emit("file.create");
this.$events.$emit("model.update");
this.$store.dispatch("notification/success", ":)");
this.$panel.notification.success();
},
replace(file) {
this.$refs.upload.open({
Expand Down
4 changes: 2 additions & 2 deletions panel/src/components/Sections/PagesSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ export default {
try {
await this.$api.pages.changeStatus(element.id, "listed", position);
this.$store.dispatch("notification/success", ":)");
this.$panel.notification.success();
this.$events.$emit("page.sort", element);
} catch (error) {
this.$store.dispatch("notification/error", {
this.$panel.notification.error({
message: error.message,
details: error.details
});
Expand Down
2 changes: 1 addition & 1 deletion panel/src/components/Views/FileView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export default {
}
},
onUpload() {
this.$store.dispatch("notification/success", ":)");
this.$panel.notification.success();
this.$reload();
}
}
Expand Down
4 changes: 2 additions & 2 deletions panel/src/components/Views/InstallationView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ export default {
globals: ["$system", "$translation"]
});
this.$store.dispatch("notification/success", this.$t("welcome") + "!");
this.$panel.notification.success(this.$t("welcome") + "!");
} catch (error) {
this.$store.dispatch("notification/error", error);
this.$panel.notification.error(error);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion panel/src/components/Views/ResetPasswordView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export default {
this.values.password
);
this.$store.dispatch("notification/success", ":)");
this.$panel.notification.success();
this.$go("/");
} catch (error) {
this.issue = error.message;
Expand Down
4 changes: 2 additions & 2 deletions panel/src/components/Views/UserView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export default {
async deleteAvatar() {
await this.$api.users.deleteAvatar(this.model.id);
this.avatar = null;
this.$store.dispatch("notification/success", ":)");
this.$panel.notification.success();
this.$reload();
},
onAvatar() {
Expand All @@ -150,7 +150,7 @@ export default {
}
},
uploadedAvatar() {
this.$store.dispatch("notification/success", ":)");
this.$panel.notification.success();
this.$reload();
}
}
Expand Down
4 changes: 2 additions & 2 deletions panel/src/config/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export default {
window.console.error(error);
}
},
onParserError: ({ html, silent }) => {
store.dispatch("fatal", { html, silent });
onParserError: ({ html }) => {
window.panel.notification.fatal(html);
},
onPrepare: (options) => {
// if language set, add to headers
Expand Down
8 changes: 3 additions & 5 deletions panel/src/config/errorhandling.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import store from "@/store/store.js";

export default {
install(app) {
window.panel = window.panel || {};
Expand All @@ -12,12 +10,12 @@ export default {
*/
window.onunhandledrejection = (event) => {
event.preventDefault();
store.dispatch("notification/error", event.reason);
window.panel.notification.error(event.reason);
};

// global deprecation handler
window.panel.deprecated = (message) => {
store.dispatch("notification/deprecated", message);
window.panel.notification.deprecated(message);
};

/**
Expand All @@ -26,7 +24,7 @@ export default {
* @param {Error} error
*/
window.panel.error = app.config.errorHandler = (error) => {
store.dispatch("notification/error", error);
window.panel.notification.error(error);
};
}
};
7 changes: 2 additions & 5 deletions panel/src/fiber/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,8 @@ export default {
*
* @param {object}
*/
onFatal({ text, options }) {
this.$store.dispatch("fatal", {
html: text,
silent: options.silent
});
onFatal({ text }) {
window.panel.notification.fatal(text);
},
/**
* Is being called when a Fiber request
Expand Down
17 changes: 15 additions & 2 deletions panel/src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Vue, { h } from "vue";
import Vue, { h, reactive } from "vue";

import Api from "./config/api.js";
import App from "./fiber/app.js";
Expand All @@ -9,6 +9,7 @@ import Fiber from "./fiber/plugin.js";
import Helpers from "./helpers/index.js";
import I18n from "./config/i18n.js";
import Libraries from "./libraries/index.js";
import Notification from "./panel/notification.js";
import Plugins from "./config/plugins.js";
import store from "./store/store.js";
import Vuelidate from "vuelidate";
Expand All @@ -19,9 +20,21 @@ Vue.config.devtools = true;
const app = new Vue({
store,
created() {
window.panel.$vue = window.panel.app = this;
window.panel.plugins.created.forEach((plugin) => plugin(this));
window.panel.$vue = window.panel.app = this;
this.$store.dispatch("content/init");

/**
* This is temporary panel setup
* code until the entire panel.js class is there
*/
window.panel.notification = Notification({
debug: window.panel.$config.debug
});

reactive(window.panel.notification);

Vue.prototype.$panel = window.panel;
},
render: () => h(App)
});
Expand Down
102 changes: 102 additions & 0 deletions panel/src/panel/module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { isObject } from "@/helpers/object";

/**
* Panel modules represent a particular part of state
* for the panel. I.e. $system, $translation.
* Features are built upon such state modules
*
* The inheritance cascade is:
* Module -> Feature -> Island
*
* @param {Object} panel The panel singleton
* @param {String} key Sets the $key for the module. Backend responses use this key.
* @param {Object} defaults Sets the default state of the module
*/
export default (key, defaults = {}) => {
return {
/**
* Module defaults will be reactive and
* must be present immediately in the object
* to get reactivity out of the box.
*/
...defaults,

/**
* The key is used to place the module
* state in the right place within the global
* panel state
*
* @returns {String}
*/
key() {
return key;
},

/**
* Returns all default values.
* This will be used to restore the state
* and fetch the existing state.
*
* @returns {Object}
*/
defaults() {
return defaults;
},

/**
* Restores the default state
*/
reset() {
return this.set(this.defaults());
},

/**
* Sets a new state for the module
*
* @param {Object} state
*/
set(state) {
this.validateState(state);

// merge the new state with the defaults
// to always get a full object with all props
for (const prop in this.defaults()) {
this[prop] = state[prop] ?? this.defaults()[prop];
}

return this.state();
},

/**
* Returns the current state. The defaults
* object is used to fetch all keys from the object
* Keys which are not defined in the defaults
* object will also not be in the final state
*
* @returns {Object}
*/
state() {
const state = {};

for (const prop in this.defaults()) {
state[prop] = this[prop] ?? this.defaults()[prop];
}

return state;
},

/**
* Validates the state object
*
* @param {Object} state
* @returns {Boolean}
*/
validateState(state) {
if (isObject(state) === false) {
throw new Error(`Invalid ${this.key()} state`);
}

return true;
}
};
};
Loading

0 comments on commit 8314b63

Please sign in to comment.