0.5 ? pleromaTan : pleromaTanFox,
+ showingMore: false,
+ contentHeight: 0
+ }
+ },
+ components: {
+ Modal
+ },
+ computed: {
+ pleromaTanStyles () {
+ return {
+ 'shape-outside': 'url(' + this.pleromaTanVariant + ')'
+ }
+ },
+ dynamicStyles () {
+ return {
+ '--____extraInfoGroupHeight': this.contentHeight + 'px'
+ }
+ },
+ shouldShow () {
+ return !this.$store.state.instance.disableUpdateNotification &&
+ this.$store.state.users.currentUser &&
+ this.$store.state.serverSideStorage.flagStorage.updateCounter < CURRENT_UPDATE_COUNTER &&
+ !this.$store.state.serverSideStorage.flagStorage.dontShowUpdateNotifs
+ }
+ },
+ methods: {
+ toggleShow () {
+ this.showingMore = !this.showingMore
+ },
+ neverShowAgain () {
+ this.toggleShow()
+ this.$store.commit('setFlag', { flag: 'updateCounter', value: CURRENT_UPDATE_COUNTER })
+ this.$store.commit('setFlag', { flag: 'dontShowUpdateNotifs', value: 1 })
+ this.$store.dispatch('pushServerSideStorage')
+ },
+ dismiss () {
+ this.$store.commit('setFlag', { flag: 'updateCounter', value: CURRENT_UPDATE_COUNTER })
+ this.$store.dispatch('pushServerSideStorage')
+ }
+ },
+ mounted () {
+ setTimeout(() => {
+ this.contentHeight = this.$refs.animatedText.scrollHeight
+ }, 1000)
+ }
+}
+
+export default UpdateNotification
diff --git a/src/components/update_notification/update_notification.scss b/src/components/update_notification/update_notification.scss
new file mode 100644
index 000000000..8cad1bc73
--- /dev/null
+++ b/src/components/update_notification/update_notification.scss
@@ -0,0 +1,107 @@
+@import 'src/_variables.scss';
+.UpdateNotification {
+ overflow: hidden;
+}
+
+.UpdateNotificationModal {
+ --__top-fringe: 15em; // how much pleroma-tan should stick her head above
+ --__bottom-fringe: 80em; // just reserving as much as we can, number is mostly irrelevant
+ --__right-fringe: 8em;
+
+ font-size: 15px;
+ position: relative;
+ transition: transform;
+ transition-timing-function: ease-in-out;
+ transition-duration: 500ms;
+
+ .text {
+ max-width: 40em;
+ padding-left: 1em;
+ }
+
+ @media all and (max-width: 800px) {
+ /* For mobile, the modal takes 100% of the available screen.
+ This ensures the minimized modal is always 50px above the browser bottom bar regardless of whether or not it is visible.
+ */
+ width: 100vw;
+ }
+
+ @media all and (max-height: 600px) {
+ display: none;
+ }
+
+ .content {
+ overflow: hidden;
+ margin-top: calc(-1 * var(--__top-fringe));
+ margin-bottom: calc(-1 * var(--__bottom-fringe));
+ margin-right: calc(-1 * var(--__right-fringe));
+ }
+
+ .panel-body {
+ border-width: 0 0 1px 0;
+ border-style: solid;
+ border-color: var(--border, $fallback--border);
+ }
+
+ .panel-footer {
+ z-index: 22;
+ position: relative;
+ border-width: 0;
+ grid-template-columns: auto;
+ }
+
+ .pleroma-tan {
+ object-fit: cover;
+ object-position: top;
+ transition: position, left, right, top, bottom, max-width, max-height;
+ transition-timing-function: ease-in-out;
+ transition-duration: 500ms;
+ width: 25em;
+ float: right;
+ z-index: 20;
+ position: relative;
+ shape-margin: 0.5em;
+ filter: drop-shadow(5px 5px 10px rgba(0,0,0,0.5));
+ pointer-events: none;
+ }
+
+ .spacer-top {
+ min-height: var(--__top-fringe);
+ }
+
+ .spacer-bottom {
+ min-height: var(--__bottom-fringe);
+ }
+
+ .extra-info-group {
+ transition: max-height, padding, height;
+ transition-timing-function: ease-in-out;
+ transition-duration: 500ms;
+ max-height: calc(var(--____extraInfoGroupHeight) + 1em); // include bottom padding
+ mask:
+ linear-gradient(to top, white, transparent) bottom/100% 2px no-repeat,
+ linear-gradient(to top, white, white);
+ }
+
+ .art-credit {
+ text-align: right;
+ }
+
+ &.-peek {
+ /* Explanation:
+ * 100vh - 100% = Distance between modal's top+bottom boundaries and screen
+ * (100vh - 100%) / 2 = Distance between bottom (or top) boundary and screen
+ */
+ transform: translateY(calc(((100vh - 100%) / 2)));
+
+ .pleroma-tan {
+ float: right;
+ z-index: 10;
+ shape-image-threshold: 0.7;
+ }
+
+ .extra-info-group {
+ max-height: 0;
+ }
+ }
+}
diff --git a/src/components/update_notification/update_notification.vue b/src/components/update_notification/update_notification.vue
new file mode 100644
index 000000000..d0e2499c8
--- /dev/null
+++ b/src/components/update_notification/update_notification.vue
@@ -0,0 +1,100 @@
+
+
+
+
+
+ {{ $t('update.big_update_title') }}
+
+
+
+
+
![]()
+
+
+
+ {{ $t('update.big_update_content') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/i18n/en.json b/src/i18n/en.json
index c54d47501..ae3be4432 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -71,6 +71,7 @@
"optional": "optional",
"show_more": "Show more",
"show_less": "Show less",
+ "never_show_again": "Never show again",
"dismiss": "Dismiss",
"cancel": "Cancel",
"disable": "Disable",
@@ -961,5 +962,14 @@
},
"display_date": {
"today": "Today"
+ },
+ "update": {
+ "big_update_title": "Please bear with us",
+ "big_update_content": "We haven't had a release in a while, so things might look and feel different than what you're used to.",
+ "update_bugs": "Please report any issues and bugs on {pleromaGitlab}, as we have changed a lot, and although we test thoroughly and use development versions ourselves, we may have missed some things. We welcome your feedback and suggestions on issues you might encounter, or how to improve Pleroma and Pleroma-FE.",
+ "update_bugs_gitlab": "Pleroma GitLab",
+ "update_changelog": "For more details on what's changed, see {theFullChangelog}.",
+ "update_changelog_here": "the full changelog",
+ "art_by": "Art by {linkToArtist}"
}
}
diff --git a/src/lib/persisted_state.js b/src/lib/persisted_state.js
index c73a38ecf..6d59c5955 100644
--- a/src/lib/persisted_state.js
+++ b/src/lib/persisted_state.js
@@ -17,6 +17,7 @@ const saveImmedeatelyActions = [
'markNotificationsAsSeen',
'clearCurrentUser',
'setCurrentUser',
+ 'setServerSideStorage',
'setHighlight',
'setOption',
'setClientData',
diff --git a/src/main.js b/src/main.js
index eacd554cf..fc798ed60 100644
--- a/src/main.js
+++ b/src/main.js
@@ -10,6 +10,7 @@ import usersModule from './modules/users.js'
import apiModule from './modules/api.js'
import configModule from './modules/config.js'
import serverSideConfigModule from './modules/serverSideConfig.js'
+import serverSideStorageModule from './modules/serverSideStorage.js'
import shoutModule from './modules/shout.js'
import oauthModule from './modules/oauth.js'
import authFlowModule from './modules/auth_flow.js'
@@ -42,6 +43,7 @@ messages.setLanguage(i18n, currentLocale)
const persistedStateOptions = {
paths: [
+ 'serverSideStorage.cache',
'config',
'users.lastLoginName',
'oauth'
@@ -73,6 +75,7 @@ const persistedStateOptions = {
api: apiModule,
config: configModule,
serverSideConfig: serverSideConfigModule,
+ serverSideStorage: serverSideStorageModule,
shout: shoutModule,
oauth: oauthModule,
authFlow: authFlowModule,
diff --git a/src/modules/instance.js b/src/modules/instance.js
index 220463ca4..bfce6f384 100644
--- a/src/modules/instance.js
+++ b/src/modules/instance.js
@@ -41,6 +41,7 @@ const defaultState = {
logoMargin: '.2em',
logoMask: true,
logoLeft: false,
+ disableUpdateNotification: false,
minimalScopesMode: false,
nsfwCensorImage: undefined,
postContentType: 'text/plain',
diff --git a/src/modules/serverSideStorage.js b/src/modules/serverSideStorage.js
new file mode 100644
index 000000000..e516a6e62
--- /dev/null
+++ b/src/modules/serverSideStorage.js
@@ -0,0 +1,230 @@
+import { toRaw } from 'vue'
+import { isEqual, cloneDeep } from 'lodash'
+import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js'
+
+export const VERSION = 1
+export const NEW_USER_DATE = new Date('2022-08-04') // date of writing this, basically
+
+export const COMMAND_TRIM_FLAGS = 1000
+export const COMMAND_TRIM_FLAGS_AND_RESET = 1001
+
+export const defaultState = {
+ // do we need to update data on server?
+ dirty: false,
+ // storage of flags - stuff that can only be set and incremented
+ flagStorage: {
+ updateCounter: 0, // Counter for most recent update notification seen
+ // TODO move to prefsStorage when that becomes a thing since only way
+ // this can be reset is by complete reset of all flags
+ dontShowUpdateNotifs: 0, // if user chose to not show update notifications ever again
+ reset: 0 // special flag that can be used to force-reset all flags, debug purposes only
+ // special reset codes:
+ // 1000: trim keys to those known by currently running FE
+ // 1001: same as above + reset everything to 0
+ },
+ // raw data
+ raw: null,
+ // local cache
+ cache: null
+}
+
+export const newUserFlags = {
+ ...defaultState.flagStorage,
+ updateCounter: CURRENT_UPDATE_COUNTER // new users don't need to see update notification
+}
+
+const _wrapData = (data) => ({
+ ...data,
+ _timestamp: Date.now(),
+ _version: VERSION
+})
+
+const _checkValidity = (data) => data._timestamp > 0 && data._version > 0
+
+export const _getRecentData = (cache, live) => {
+ const result = { recent: null, stale: null, needUpload: false }
+ const cacheValid = _checkValidity(cache || {})
+ const liveValid = _checkValidity(live || {})
+ if (!liveValid && cacheValid) {
+ result.needUpload = true
+ console.debug('Nothing valid stored on server, assuming cache to be source of truth')
+ result.recent = cache
+ result.stale = live
+ } else if (!cacheValid && liveValid) {
+ console.debug('Valid storage on server found, no local cache found, using live as source of truth')
+ result.recent = live
+ result.stale = cache
+ } else if (cacheValid && liveValid) {
+ console.debug('Both sources have valid data, figuring things out...')
+ if (live._timestamp === cache._timestamp && live._version === cache._version) {
+ console.debug('Same version/timestamp on both source, source of truth irrelevant')
+ result.recent = cache
+ result.stale = live
+ } else {
+ console.debug('Different timestamp, figuring out which one is more recent')
+ if (live._timestamp < cache._timestamp) {
+ result.recent = cache
+ result.stale = live
+ } else {
+ result.recent = live
+ result.stale = cache
+ }
+ }
+ } else {
+ console.debug('Both sources are invalid, start from scratch')
+ result.needUpload = true
+ }
+ return result
+}
+
+export const _getAllFlags = (recent, stale) => {
+ return Array.from(new Set([
+ ...Object.keys(toRaw((recent || {}).flagStorage || {})),
+ ...Object.keys(toRaw((stale || {}).flagStorage || {}))
+ ]))
+}
+
+export const _mergeFlags = (recent, stale, allFlagKeys) => {
+ return Object.fromEntries(allFlagKeys.map(flag => {
+ const recentFlag = recent.flagStorage[flag]
+ const staleFlag = stale.flagStorage[flag]
+ // use flag that is of higher value
+ return [flag, Number((recentFlag > staleFlag ? recentFlag : staleFlag) || 0)]
+ }))
+}
+
+export const _resetFlags = (totalFlags, knownKeys = defaultState.flagStorage) => {
+ let result = { ...totalFlags }
+ const allFlagKeys = Object.keys(totalFlags)
+ // flag reset functionality
+ if (totalFlags.reset >= COMMAND_TRIM_FLAGS && totalFlags.reset <= COMMAND_TRIM_FLAGS_AND_RESET) {
+ console.debug('Received command to trim the flags')
+ const knownKeysSet = new Set(Object.keys(knownKeys))
+
+ // Trim
+ result = {}
+ allFlagKeys.forEach(flag => {
+ if (knownKeysSet.has(flag)) {
+ result[flag] = totalFlags[flag]
+ }
+ })
+
+ // Reset
+ if (totalFlags.reset === COMMAND_TRIM_FLAGS_AND_RESET) {
+ // 1001 - and reset everything to 0
+ console.debug('Received command to reset the flags')
+ Object.keys(knownKeys).forEach(flag => { result[flag] = 0 })
+ }
+ } else if (totalFlags.reset > 0 && totalFlags.reset < 9000) {
+ console.debug('Received command to reset the flags')
+ allFlagKeys.forEach(flag => { result[flag] = 0 })
+ }
+ result.reset = 0
+ return result
+}
+
+export const _doMigrations = (cache) => {
+ if (!cache) return cache
+
+ if (cache._version < VERSION) {
+ console.debug('Local cached data has older version, seeing if there any migrations that can be applied')
+
+ // no migrations right now since we only have one version
+ console.debug('No migrations found')
+ }
+
+ if (cache._version > VERSION) {
+ console.debug('Local cached data has newer version, seeing if there any reverse migrations that can be applied')
+
+ // no reverse migrations right now but we leave a possibility of loading a hotpatch if need be
+ if (window._PLEROMA_HOTPATCH) {
+ if (window._PLEROMA_HOTPATCH.reverseMigrations) {
+ console.debug('Found hotpatch migration, applying')
+ return window._PLEROMA_HOTPATCH.reverseMigrations.call({}, 'serverSideStorage', { from: cache._version, to: VERSION }, cache)
+ }
+ }
+ }
+
+ return cache
+}
+
+export const mutations = {
+ setServerSideStorage (state, userData) {
+ const live = userData.storage
+ state.raw = live
+ let cache = state.cache
+
+ cache = _doMigrations(cache)
+
+ let { recent, stale, needsUpload } = _getRecentData(cache, live)
+
+ const userNew = userData.created_at > NEW_USER_DATE
+ const flagsTemplate = userNew ? newUserFlags : defaultState.flagStorage
+ let dirty = false
+
+ if (recent === null) {
+ console.debug(`Data is empty, initializing for ${userNew ? 'new' : 'existing'} user`)
+ recent = _wrapData({
+ flagStorage: { ...flagsTemplate }
+ })
+ }
+
+ if (!needsUpload && recent && stale) {
+ console.debug('Checking if data needs merging...')
+ // discarding timestamps and versions
+ const { _timestamp: _0, _version: _1, ...recentData } = recent
+ const { _timestamp: _2, _version: _3, ...staleData } = stale
+ dirty = !isEqual(recentData, staleData)
+ console.debug(`Data ${dirty ? 'needs' : 'doesn\'t need'} merging`)
+ }
+
+ const allFlagKeys = _getAllFlags(recent, stale)
+ let totalFlags
+ if (dirty) {
+ // Merge the flags
+ console.debug('Merging the flags...')
+ totalFlags = _mergeFlags(recent, stale, allFlagKeys)
+ } else {
+ totalFlags = recent.flagStorage
+ }
+
+ totalFlags = _resetFlags(totalFlags)
+
+ recent.flagStorage = totalFlags
+
+ state.dirty = dirty || needsUpload
+ state.cache = recent
+ // set local timestamp to smaller one if we don't have any changes
+ if (stale && recent && !state.dirty) {
+ state.cache._timestamp = Math.min(stale._timestamp, recent._timestamp)
+ }
+ state.flagStorage = state.cache.flagStorage
+ },
+ setFlag (state, { flag, value }) {
+ state.flagStorage[flag] = value
+ state.dirty = true
+ }
+}
+
+const serverSideStorage = {
+ state: {
+ ...cloneDeep(defaultState)
+ },
+ mutations,
+ actions: {
+ pushServerSideStorage ({ state, rootState, commit }, { force = false } = {}) {
+ const needPush = state.dirty || force
+ if (!needPush) return
+ state.cache = _wrapData({
+ flagStorage: toRaw(state.flagStorage)
+ })
+ const params = { pleroma_settings_store: { 'pleroma-fe': state.cache } }
+ rootState.api.backendInteractor
+ .updateProfile({ params })
+ .then((user) => commit('setServerSideStorage', user))
+ state.dirty = false
+ }
+ }
+}
+
+export default serverSideStorage
diff --git a/src/modules/users.js b/src/modules/users.js
index 13d4e318e..b6fb9746a 100644
--- a/src/modules/users.js
+++ b/src/modules/users.js
@@ -525,6 +525,7 @@ const users = {
user.muteIds = []
user.domainMutes = []
commit('setCurrentUser', user)
+ commit('setServerSideStorage', user)
commit('addNewUsers', [user])
store.dispatch('fetchEmoji')
@@ -534,6 +535,7 @@ const users = {
// Set our new backend interactor
commit('setBackendInteractor', backendInteractorService(accessToken))
+ store.dispatch('pushServerSideStorage')
if (user.token) {
store.dispatch('setWsToken', user.token)
diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js
index 7f831ed90..e6a87b63f 100644
--- a/src/services/entity_normalizer/entity_normalizer.service.js
+++ b/src/services/entity_normalizer/entity_normalizer.service.js
@@ -90,6 +90,9 @@ export const parseUser = (data) => {
output.bot = data.bot
if (data.pleroma) {
+ if (data.pleroma.settings_store) {
+ output.storage = data.pleroma.settings_store['pleroma-fe']
+ }
const relationship = data.pleroma.relationship
output.background_image = data.pleroma.background_image
diff --git a/static/config.json b/static/config.json
index c737fad8e..25a2541cf 100644
--- a/static/config.json
+++ b/static/config.json
@@ -14,6 +14,7 @@
"logoMask": true,
"logoLeft": false,
"minimalScopesMode": false,
+ "disableUpdateNotification": false,
"nsfwCensorImage": "",
"postContentType": "text/plain",
"redirectRootLogin": "/main/friends",
diff --git a/test/unit/specs/modules/serverSideStorage.spec.js b/test/unit/specs/modules/serverSideStorage.spec.js
new file mode 100644
index 000000000..e06c6ada0
--- /dev/null
+++ b/test/unit/specs/modules/serverSideStorage.spec.js
@@ -0,0 +1,178 @@
+import { cloneDeep } from 'lodash'
+
+import {
+ VERSION,
+ COMMAND_TRIM_FLAGS,
+ COMMAND_TRIM_FLAGS_AND_RESET,
+ _getRecentData,
+ _getAllFlags,
+ _mergeFlags,
+ _resetFlags,
+ mutations,
+ defaultState,
+ newUserFlags
+} from 'src/modules/serverSideStorage.js'
+
+describe('The serverSideStorage module', () => {
+ describe('mutations', () => {
+ describe('setServerSideStorage', () => {
+ const { setServerSideStorage } = mutations
+ const user = {
+ created_at: new Date('1999-02-09'),
+ storage: {}
+ }
+
+ it('should initialize storage if none present', () => {
+ const state = cloneDeep(defaultState)
+ setServerSideStorage(state, user)
+ expect(state.cache._version).to.eql(VERSION)
+ expect(state.cache._timestamp).to.be.a('number')
+ expect(state.cache.flagStorage).to.eql(defaultState.flagStorage)
+ })
+
+ it('should initialize storage with proper flags for new users if none present', () => {
+ const state = cloneDeep(defaultState)
+ setServerSideStorage(state, { ...user, created_at: new Date() })
+ expect(state.cache._version).to.eql(VERSION)
+ expect(state.cache._timestamp).to.be.a('number')
+ expect(state.cache.flagStorage).to.eql(newUserFlags)
+ })
+
+ it('should merge flags even if remote timestamp is older', () => {
+ const state = {
+ ...cloneDeep(defaultState),
+ cache: {
+ _timestamp: Date.now(),
+ _version: VERSION,
+ ...cloneDeep(defaultState)
+ }
+ }
+ setServerSideStorage(
+ state,
+ {
+ ...user,
+ storage: {
+ _timestamp: 123,
+ _version: VERSION,
+ flagStorage: {
+ ...defaultState.flagStorage,
+ updateCounter: 1
+ }
+ }
+ }
+ )
+ expect(state.cache.flagStorage).to.eql({
+ ...defaultState.flagStorage,
+ updateCounter: 1
+ })
+ })
+
+ it('should reset local timestamp to remote if contents are the same', () => {
+ const state = {
+ ...cloneDeep(defaultState),
+ cache: null
+ }
+ setServerSideStorage(
+ state,
+ {
+ ...user,
+ storage: {
+ _timestamp: 123,
+ _version: VERSION,
+ flagStorage: {
+ ...defaultState.flagStorage,
+ updateCounter: 999
+ }
+ }
+ }
+ )
+ expect(state.cache._timestamp).to.eql(123)
+ expect(state.flagStorage.updateCounter).to.eql(999)
+ expect(state.cache.flagStorage.updateCounter).to.eql(999)
+ })
+
+ it('should remote version if local missing', () => {
+ const state = cloneDeep(defaultState)
+ setServerSideStorage(state, user)
+ expect(state.cache._version).to.eql(VERSION)
+ expect(state.cache._timestamp).to.be.a('number')
+ expect(state.cache.flagStorage).to.eql(defaultState.flagStorage)
+ })
+ })
+ })
+
+ describe('helper functions', () => {
+ describe('_getRecentData', () => {
+ it('should handle nulls correctly', () => {
+ expect(_getRecentData(null, null)).to.eql({ recent: null, stale: null, needUpload: true })
+ })
+
+ it('doesn\'t choke on invalid data', () => {
+ expect(_getRecentData({ a: 1 }, { b: 2 })).to.eql({ recent: null, stale: null, needUpload: true })
+ })
+
+ it('should prefer the valid non-null correctly, needUpload works properly', () => {
+ const nonNull = { _version: VERSION, _timestamp: 1 }
+ expect(_getRecentData(nonNull, null)).to.eql({ recent: nonNull, stale: null, needUpload: true })
+ expect(_getRecentData(null, nonNull)).to.eql({ recent: nonNull, stale: null, needUpload: false })
+ })
+
+ it('should prefer the one with higher timestamp', () => {
+ const a = { _version: VERSION, _timestamp: 1 }
+ const b = { _version: VERSION, _timestamp: 2 }
+
+ expect(_getRecentData(a, b)).to.eql({ recent: b, stale: a, needUpload: false })
+ expect(_getRecentData(b, a)).to.eql({ recent: b, stale: a, needUpload: false })
+ })
+
+ it('case where both are same', () => {
+ const a = { _version: VERSION, _timestamp: 3 }
+ const b = { _version: VERSION, _timestamp: 3 }
+
+ expect(_getRecentData(a, b)).to.eql({ recent: b, stale: a, needUpload: false })
+ expect(_getRecentData(b, a)).to.eql({ recent: b, stale: a, needUpload: false })
+ })
+ })
+
+ describe('_getAllFlags', () => {
+ it('should handle nulls properly', () => {
+ expect(_getAllFlags(null, null)).to.eql([])
+ })
+ it('should output list of keys if passed single object', () => {
+ expect(_getAllFlags({ flagStorage: { a: 1, b: 1, c: 1 } }, null)).to.eql(['a', 'b', 'c'])
+ })
+ it('should union keys of both objects', () => {
+ expect(_getAllFlags({ flagStorage: { a: 1, b: 1, c: 1 } }, { flagStorage: { c: 1, d: 1 } })).to.eql(['a', 'b', 'c', 'd'])
+ })
+ })
+
+ describe('_mergeFlags', () => {
+ it('should handle merge two flag sets correctly picking higher numbers', () => {
+ expect(
+ _mergeFlags(
+ { flagStorage: { a: 0, b: 3 } },
+ { flagStorage: { b: 1, c: 4, d: 9 } },
+ ['a', 'b', 'c', 'd'])
+ ).to.eql({ a: 0, b: 3, c: 4, d: 9 })
+ })
+ })
+
+ describe('_resetFlags', () => {
+ it('should reset all known flags to 0 when reset flag is set to > 0 and < 9000', () => {
+ const totalFlags = { a: 0, b: 3, reset: 1 }
+
+ expect(_resetFlags(totalFlags)).to.eql({ a: 0, b: 0, reset: 0 })
+ })
+ it('should trim all flags to known when reset is set to 1000', () => {
+ const totalFlags = { a: 0, b: 3, c: 33, reset: COMMAND_TRIM_FLAGS }
+
+ expect(_resetFlags(totalFlags, { a: 0, b: 0, reset: 0 })).to.eql({ a: 0, b: 3, reset: 0 })
+ })
+ it('should trim all flags to known and reset when reset is set to 1001', () => {
+ const totalFlags = { a: 0, b: 3, c: 33, reset: COMMAND_TRIM_FLAGS_AND_RESET }
+
+ expect(_resetFlags(totalFlags, { a: 0, b: 0, reset: 0 })).to.eql({ a: 0, b: 0, reset: 0 })
+ })
+ })
+ })
+})
diff --git a/yarn.lock b/yarn.lock
index 5da002e44..b034ae119 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1375,29 +1375,29 @@
source-map "^0.6.1"
yaml-eslint-parser "^0.3.2"
-"@intlify/core-base@9.2.0-beta.40":
- version "9.2.0-beta.40"
- resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.2.0-beta.40.tgz#85df2e183b2102716c5d40795848fc2359354580"
- integrity sha512-vOR0lHgtJ3IkzvXLeMQeNeYreFSKG9v3SU8QOD//WKHdBy4QPISs9CZJkYzBeBVCJVZ/eM6OTSbXF8M2k53iCw==
+"@intlify/core-base@9.2.0":
+ version "9.2.0"
+ resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.2.0.tgz#e652ef7f71597c5d6b123ae34f01e106bb8c5a1b"
+ integrity sha512-PkaiY9FAzZHAwPNaS+3csXR6L5k8LFBsSjn63/dy5BqE3pOJd07R4+NYtk8ezxymUemu7p5cS9YX77cmnQO6aQ==
dependencies:
- "@intlify/devtools-if" "9.2.0-beta.40"
- "@intlify/message-compiler" "9.2.0-beta.40"
- "@intlify/shared" "9.2.0-beta.40"
- "@intlify/vue-devtools" "9.2.0-beta.40"
+ "@intlify/devtools-if" "9.2.0"
+ "@intlify/message-compiler" "9.2.0"
+ "@intlify/shared" "9.2.0"
+ "@intlify/vue-devtools" "9.2.0"
-"@intlify/devtools-if@9.2.0-beta.40":
- version "9.2.0-beta.40"
- resolved "https://registry.yarnpkg.com/@intlify/devtools-if/-/devtools-if-9.2.0-beta.40.tgz#bee42fefaaaa590aa5ac7fe2a98777fb84bfaf5e"
- integrity sha512-EUiuLxlgortD1dhT0btm3YYIs2vk9kMdcGXiYYbHWRTylc8Iv7Yz47y5Y+IlbZzk51h/nYvuqXE1h9diZZWAvQ==
+"@intlify/devtools-if@9.2.0":
+ version "9.2.0"
+ resolved "https://registry.yarnpkg.com/@intlify/devtools-if/-/devtools-if-9.2.0.tgz#55249fcea03b08a6c4d5f04a253f086c74090595"
+ integrity sha512-8yfusyhUaqInnn6Cma+NTTh5+EWyrnAkez36qADetbUnY4tCeGyAy+MmIAGh0uqmJVIeX94vd6L1AaA0p9McGg==
dependencies:
- "@intlify/shared" "9.2.0-beta.40"
+ "@intlify/shared" "9.2.0"
-"@intlify/message-compiler@9.2.0-beta.40":
- version "9.2.0-beta.40"
- resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.2.0-beta.40.tgz#d5d0c5652b9e74e0b4da07a2b8731e1f0e729029"
- integrity sha512-6QWTSYewmkew4nsRqgkwTVuGFKzxVCOK8EXsPt15N+tN1g+OYjC3PfGA2dPB6cVkNxqA9mV/hNK02uHPWU9t0A==
+"@intlify/message-compiler@9.2.0":
+ version "9.2.0"
+ resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.2.0.tgz#0516f144bed8274b3ea4c9eede4b9a6c08fd046d"
+ integrity sha512-KGwwZsl+Nw2O26ZOKdytncxzKnMZ236KmM70u4GePgbizI+pu8yAh0apKxljSPzEJ7WECKTVc9R+laG12EJQYA==
dependencies:
- "@intlify/shared" "9.2.0-beta.40"
+ "@intlify/shared" "9.2.0"
source-map "0.6.1"
"@intlify/message-compiler@next":
@@ -1408,23 +1408,23 @@
"@intlify/shared" "9.2.0-beta.34"
source-map "0.6.1"
+"@intlify/shared@9.2.0":
+ version "9.2.0"
+ resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.2.0.tgz#bcd026e419a9eb2e577afe520481ceaca80b3aa9"
+ integrity sha512-71uObL3Sy2ZiBQBMVETbkspE4Plpy87Hvlj6FAUF3xdD+M82tuxe3MVJjaD3ucqhtHmQWBkAWEurVLdPYr8G2g==
+
"@intlify/shared@9.2.0-beta.34", "@intlify/shared@next":
version "9.2.0-beta.34"
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.2.0-beta.34.tgz#e8e9a93455eadcc9785fe2e2437fe037fc267f7d"
integrity sha512-hbUKcVbTOkLVpnlSeZE1OPgEI7FpvhuZF/gb84xECTjXEImIa3u0fIcJKUUffv3dlAx8fMOE5xKgDzngidm0tw==
-"@intlify/shared@9.2.0-beta.40":
- version "9.2.0-beta.40"
- resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.2.0-beta.40.tgz#a850936008e6e865310b2a49136d494dd326faab"
- integrity sha512-xWz+SFjgt/LfaSbbHVn+V7gmvX4ZNP3cIFta790GWZ/tEgwJeC3tkV7i45iUbZ4ZimOerFgKH05b7qvJlKb6RQ==
-
-"@intlify/vue-devtools@9.2.0-beta.40":
- version "9.2.0-beta.40"
- resolved "https://registry.yarnpkg.com/@intlify/vue-devtools/-/vue-devtools-9.2.0-beta.40.tgz#37457fd719b0b6afb0679c33ceb47b0ac77f457c"
- integrity sha512-3A0D/E9quf+KWonzXUDk3xNP0+d1DMdtAwyXNTjzFcQPvjugC2Xn6fmsd0kNn7nHjgpB+vwIuamGiuE+S+OULw==
+"@intlify/vue-devtools@9.2.0":
+ version "9.2.0"
+ resolved "https://registry.yarnpkg.com/@intlify/vue-devtools/-/vue-devtools-9.2.0.tgz#fb7f18e83378da3504d500210ce3994552e18d18"
+ integrity sha512-6P/tE/JdNNVo1z/sr/FnSd90OVlox2XuKMmzHR13nvpHGX7fjRn6pVv47L2jySOYG1cMezmYvJl14TAddKpw5Q==
dependencies:
- "@intlify/core-base" "9.2.0-beta.40"
- "@intlify/shared" "9.2.0-beta.40"
+ "@intlify/core-base" "9.2.0"
+ "@intlify/shared" "9.2.0"
"@intlify/vue-i18n-loader@^5.0.0":
version "5.0.0"
@@ -10403,14 +10403,14 @@ vue-eslint-parser@^9.0.1:
lodash "^4.17.21"
semver "^7.3.6"
-vue-i18n@9.2.0-beta.40:
- version "9.2.0-beta.40"
- resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.2.0-beta.40.tgz#8088b19d619f47bf0e0f529f9bd1413460f2ff56"
- integrity sha512-UwcGsbTTaDJry6BbFFzt115EVHN/bXi07DyUIZ4zrYeGMBPp2QAptMwVaGUQid1gaMmUreAKarGIqw46oCQEvg==
+vue-i18n@9.2.0:
+ version "9.2.0"
+ resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.2.0.tgz#18e746aa0d6739860fe73383daff8884b6fd3c47"
+ integrity sha512-vN8aW8Vn4r4eRhUgTLK1/kCIYMb3LQmuloa1YP4NsehiASCX0XCq50tgUAz1o0eBAZ8YvhdlaVPqlO7O0wCnkQ==
dependencies:
- "@intlify/core-base" "9.2.0-beta.40"
- "@intlify/shared" "9.2.0-beta.40"
- "@intlify/vue-devtools" "9.2.0-beta.40"
+ "@intlify/core-base" "9.2.0"
+ "@intlify/shared" "9.2.0"
+ "@intlify/vue-devtools" "9.2.0"
"@vue/devtools-api" "^6.2.1"
vue-loader@^16.0.0: