feat: Show banner message if Chatwoot update available (#3999)
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Nithin David Thomas <1277421+nithindavid@users.noreply.github.com> Co-authored-by: Fayaz Ahmed <15716057+fayazara@users.noreply.github.com> Co-authored-by: Pranav <pranav@chatwoot.com>
This commit is contained in:
parent
02dd5ecfab
commit
8c8c5a77c8
10 changed files with 156 additions and 12 deletions
|
@ -1,5 +1,6 @@
|
|||
<template>
|
||||
<div id="app" class="app-wrapper app-root">
|
||||
<update-banner :latest-chatwoot-version="latestChatwootVersion" />
|
||||
<transition name="fade" mode="out-in">
|
||||
<router-view></router-view>
|
||||
</transition>
|
||||
|
@ -13,24 +14,27 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { accountIdFromPathname } from './helper/URLHelper';
|
||||
import { mapGetters } from 'vuex';
|
||||
import AddAccountModal from '../dashboard/components/layout/sidebarComponents/AddAccountModal';
|
||||
import WootSnackbarBox from './components/SnackbarContainer';
|
||||
import NetworkNotification from './components/NetworkNotification';
|
||||
import { accountIdFromPathname } from './helper/URLHelper';
|
||||
import UpdateBanner from './components/app/UpdateBanner.vue';
|
||||
import WootSnackbarBox from './components/SnackbarContainer';
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
|
||||
components: {
|
||||
WootSnackbarBox,
|
||||
AddAccountModal,
|
||||
NetworkNotification,
|
||||
UpdateBanner,
|
||||
WootSnackbarBox,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
showAddAccountModal: false,
|
||||
latestChatwootVersion: null,
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -38,6 +42,7 @@ export default {
|
|||
...mapGetters({
|
||||
getAccount: 'accounts/getAccount',
|
||||
currentUser: 'getCurrentUser',
|
||||
globalConfig: 'globalConfig/get',
|
||||
}),
|
||||
hasAccounts() {
|
||||
return (
|
||||
|
@ -72,8 +77,12 @@ export default {
|
|||
|
||||
if (accountId) {
|
||||
await this.$store.dispatch('accounts/get');
|
||||
const { locale } = this.getAccount(accountId);
|
||||
const {
|
||||
locale,
|
||||
latest_chatwoot_version: latestChatwootVersion,
|
||||
} = this.getAccount(accountId);
|
||||
this.setLocale(locale);
|
||||
this.latestChatwootVersion = latestChatwootVersion;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -82,6 +91,11 @@ export default {
|
|||
|
||||
<style lang="scss">
|
||||
@import './assets/scss/app';
|
||||
.update-banner {
|
||||
height: var(--space-larger);
|
||||
align-items: center;
|
||||
font-size: var(--font-size-small) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
|
||||
|
|
|
@ -10,10 +10,24 @@ body {
|
|||
|
||||
.app-wrapper {
|
||||
@include full-height;
|
||||
flex-grow: 0;
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.app-root {
|
||||
.banner + .app-wrapper {
|
||||
.button--fixed-right-top {
|
||||
top: 5.6 * $space-one;
|
||||
}
|
||||
|
||||
.off-canvas-content {
|
||||
.button--fixed-right-top {
|
||||
top: $space-small;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is-closed .app-root {
|
||||
@include flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
@ -21,6 +35,7 @@ body {
|
|||
.app-content {
|
||||
@include flex;
|
||||
@include full-height;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
|
74
app/javascript/dashboard/components/app/UpdateBanner.vue
Normal file
74
app/javascript/dashboard/components/app/UpdateBanner.vue
Normal file
|
@ -0,0 +1,74 @@
|
|||
<template>
|
||||
<banner
|
||||
v-if="shouldShowBanner"
|
||||
class="update-banner"
|
||||
color-scheme="primary"
|
||||
:banner-message="bannerMessage"
|
||||
href-link="https://github.com/chatwoot/chatwoot/releases"
|
||||
:href-link-text="$t('GENERAL_SETTINGS.LEARN_MORE')"
|
||||
has-close-button
|
||||
@close="dismissUpdateBanner"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import Banner from 'dashboard/components/ui/Banner.vue';
|
||||
import LocalStorage from '../../helper/localStorage';
|
||||
import { mapGetters } from 'vuex';
|
||||
import adminMixin from 'dashboard/mixins/isAdmin';
|
||||
|
||||
const semver = require('semver');
|
||||
const dismissedUpdates = new LocalStorage('dismissedUpdates');
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Banner,
|
||||
},
|
||||
mixins: [adminMixin],
|
||||
props: {
|
||||
latestChatwootVersion: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({ globalConfig: 'globalConfig/get' }),
|
||||
hasAnUpdateAvailable() {
|
||||
if (!semver.valid(this.latestChatwootVersion)) {
|
||||
return false;
|
||||
}
|
||||
return semver.lt(
|
||||
this.globalConfig.appVersion,
|
||||
this.latestChatwootVersion
|
||||
);
|
||||
},
|
||||
bannerMessage() {
|
||||
return this.$t('GENERAL_SETTINGS.UPDATE_CHATWOOT', {
|
||||
latestChatwootVersion: this.latestChatwootVersion,
|
||||
});
|
||||
},
|
||||
shouldShowBanner() {
|
||||
return (
|
||||
this.globalConfig.displayManifest &&
|
||||
this.hasAnUpdateAvailable &&
|
||||
!this.isVersionNotificationDismissed(this.latestChatwootVersion) &&
|
||||
this.isAdmin
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isVersionNotificationDismissed(version) {
|
||||
return dismissedUpdates.get().includes(version);
|
||||
},
|
||||
dismissUpdateBanner() {
|
||||
let updatedDismissedItems = dismissedUpdates.get();
|
||||
if (updatedDismissedItems instanceof Array) {
|
||||
updatedDismissedItems.push(this.latestChatwootVersion);
|
||||
} else {
|
||||
updatedDismissedItems = [this.latestChatwootVersion];
|
||||
}
|
||||
dismissedUpdates.store(updatedDismissedItems);
|
||||
this.latestChatwootVersion = this.globalConfig.appVersion;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -184,6 +184,8 @@ export default {
|
|||
.woot-sidebar {
|
||||
background: var(--white);
|
||||
display: flex;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ export default {
|
|||
width: var(--space-jumbo);
|
||||
border-right: 1px solid var(--s-50);
|
||||
box-sizing: content-box;
|
||||
height: 100vh;
|
||||
height: 100%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -225,7 +225,7 @@ export default {
|
|||
.secondary-menu {
|
||||
background: var(--white);
|
||||
border-right: 1px solid var(--s-50);
|
||||
height: 100vh;
|
||||
height: 100%;
|
||||
width: 19rem;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="banner" :class="bannerClasses">
|
||||
<span>
|
||||
<span class="banner-message">
|
||||
{{ bannerMessage }}
|
||||
<a
|
||||
v-if="hrefLink"
|
||||
|
@ -26,7 +26,7 @@
|
|||
v-if="hasCloseButton"
|
||||
size="small"
|
||||
variant="link"
|
||||
color-scheme="warning"
|
||||
color-scheme="secondary"
|
||||
icon="dismiss-circle"
|
||||
class-names="banner-action__button"
|
||||
@click="onClickClose"
|
||||
|
@ -92,8 +92,19 @@ export default {
|
|||
justify-content: center;
|
||||
position: sticky;
|
||||
|
||||
&.primary {
|
||||
background: var(--w-500);
|
||||
.banner-action__button {
|
||||
color: var(--white);
|
||||
}
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
background: var(--s-300);
|
||||
background: var(--s-200);
|
||||
color: var(--s-800);
|
||||
a {
|
||||
color: var(--s-800);
|
||||
}
|
||||
}
|
||||
|
||||
&.alert {
|
||||
|
@ -110,9 +121,13 @@ export default {
|
|||
|
||||
&.gray {
|
||||
background: var(--b-500);
|
||||
.banner-action__button {
|
||||
color: var(--white);
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
margin-left: var(--space-smaller);
|
||||
text-decoration: underline;
|
||||
color: var(--white);
|
||||
font-size: var(--font-size-mini);
|
||||
|
@ -125,5 +140,10 @@ export default {
|
|||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.banner-message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -100,7 +100,8 @@ export default {
|
|||
display: flex;
|
||||
background: var(--color-background-light);
|
||||
margin: 0;
|
||||
height: calc(100vh - var(--space-jumbo));
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.conversation-sidebar-wrap {
|
||||
|
|
17
app/javascript/dashboard/helper/localStorage.js
Normal file
17
app/javascript/dashboard/helper/localStorage.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
class LocalStorage {
|
||||
constructor(key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
store(allItems) {
|
||||
localStorage.setItem(this.key, JSON.stringify(allItems));
|
||||
localStorage.setItem(this.key + ':ts', Date.now());
|
||||
}
|
||||
|
||||
get() {
|
||||
let stored = localStorage.getItem(this.key);
|
||||
return JSON.parse(stored) || [];
|
||||
}
|
||||
}
|
||||
|
||||
export default LocalStorage;
|
|
@ -47,7 +47,8 @@
|
|||
"CUSTOM_EMAIL_DOMAIN_ENABLED": "You can receive emails in your custom domain now."
|
||||
}
|
||||
},
|
||||
"UPDATE_CHATWOOT": "An update %{latestChatwootVersion} for Chatwoot is available. Please update your instance."
|
||||
"UPDATE_CHATWOOT": "An update %{latestChatwootVersion} for Chatwoot is available. Please update your instance.",
|
||||
"LEARN_MORE":"Learn more"
|
||||
},
|
||||
"FORMS": {
|
||||
"MULTISELECT": {
|
||||
|
|
Loading…
Reference in a new issue