mirror of
https://github.com/Lissy93/dashy.git
synced 2025-12-06 16:43:13 +01:00
pin number functionatlity added
This commit is contained in:
parent
9c6686376e
commit
ebf45c5969
4 changed files with 235 additions and 55 deletions
70
src/components/InteractiveEditor/PinInput.vue
Normal file
70
src/components/InteractiveEditor/PinInput.vue
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<div class="pin-gate">
|
||||
<div class="pin-head">
|
||||
<i class="far fa-lock" aria-hidden="true"></i>
|
||||
<span>Locked section</span>
|
||||
</div>
|
||||
|
||||
<FormSchema
|
||||
:schema="schema"
|
||||
v-model="form"
|
||||
name="pinInputForm"
|
||||
class="pin-form"
|
||||
/>
|
||||
|
||||
<div class="pin-actions">
|
||||
<button class="pin-btn" @click="submit">Unlock</button>
|
||||
<button class="pin-reset" @click="lockAgain" type="button">Lock again</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FormSchema from '@formschema/native';
|
||||
|
||||
export default {
|
||||
name: 'PinInput',
|
||||
components: { FormSchema },
|
||||
props: {
|
||||
id: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: { pin: '' },
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
pin: {
|
||||
title: 'Enter PIN',
|
||||
type: 'string',
|
||||
attrs: {
|
||||
type: 'password',
|
||||
inputmode: 'numeric',
|
||||
autocomplete: 'one-time-code',
|
||||
placeholder: '••••',
|
||||
class: 'pin-input',
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['pin'],
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
const tried = String(this.form.pin || '');
|
||||
this.$emit('unlock_attempt', {
|
||||
pin: tried,
|
||||
id: this.id,
|
||||
});
|
||||
// clear local field
|
||||
this.form.pin = '';
|
||||
},
|
||||
lockAgain() {
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
</style>
|
||||
|
|
@ -14,6 +14,12 @@
|
|||
:id="sectionRef"
|
||||
:ref="sectionRef"
|
||||
>
|
||||
<PinInput
|
||||
v-if="showPinRequired"
|
||||
:id = "sectionRef"
|
||||
@unlock_attempt="saveUnlockPins"
|
||||
/>
|
||||
<div v-if="!showPinRequired">
|
||||
<!-- If no items, show message -->
|
||||
<div v-if="isEmpty" class="no-items">
|
||||
{{ $t('home.no-items-section') }}
|
||||
|
|
@ -72,6 +78,7 @@
|
|||
@navigateToSection="navigateToSection"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal for opening in modal view -->
|
||||
<IframeModal
|
||||
:ref="`iframeModal-${groupId}`"
|
||||
|
|
@ -109,6 +116,7 @@ import Collapsable from '@/components/LinkItems/Collapsable.vue';
|
|||
import IframeModal from '@/components/LinkItems/IframeModal.vue';
|
||||
import EditSection from '@/components/InteractiveEditor/EditSection.vue';
|
||||
import ContextMenu from '@/components/LinkItems/SectionContextMenu.vue';
|
||||
import PinInput from '@/components/InteractiveEditor/PinInput.vue';
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
import StoreKeys from '@/utils/StoreMutations';
|
||||
import {
|
||||
|
|
@ -117,12 +125,16 @@ import {
|
|||
modalNames,
|
||||
} from '@/utils/defaults';
|
||||
|
||||
const SECRET_UNLOCKED_KEY = 'dashy.secret.unlocked';
|
||||
const SECRET_PINS_KEY = 'dashy.secret.expectedPins';
|
||||
|
||||
export default {
|
||||
name: 'Section',
|
||||
props: {
|
||||
groupId: String,
|
||||
title: String,
|
||||
icon: String,
|
||||
pin: String,
|
||||
displayData: Object,
|
||||
items: Array,
|
||||
widgets: Array,
|
||||
|
|
@ -137,6 +149,7 @@ export default {
|
|||
WidgetBase,
|
||||
IframeModal,
|
||||
EditSection,
|
||||
PinInput,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -148,9 +161,11 @@ export default {
|
|||
},
|
||||
sectionWidth: 0,
|
||||
resizeObserver: null,
|
||||
isUnlocked: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
||||
appConfig() {
|
||||
return this.$store.getters.appConfig;
|
||||
},
|
||||
|
|
@ -209,6 +224,55 @@ export default {
|
|||
}
|
||||
return styles;
|
||||
},
|
||||
shouldRenderSection() {
|
||||
const hidden = this.displayData?.hiddenOnPurpose === true;
|
||||
|
||||
// Always show in Edit Mode so the user can unhide it
|
||||
if (this.isEditMode) return true;
|
||||
|
||||
// If not hidden, render as usual
|
||||
if (!hidden || this.showHiddenMode) return true;
|
||||
|
||||
// If hidden, only show when search is active AND this section has hits
|
||||
const searchActive = (this.searchTerm || '').trim().length > 0;
|
||||
const hasHits = Array.isArray(this.items) && this.items.length > 0;
|
||||
return searchActive && hasHits;
|
||||
},
|
||||
showPinRequired() {
|
||||
if (this.isEditMode) return false;
|
||||
return this.displayData.secret === true && !this.isUnlocked;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
searchTerm: {
|
||||
handler(newSeachTerm) {
|
||||
// find if special code search is used
|
||||
const showHidden = newSeachTerm.trim().toLowerCase() === '<hidden>';
|
||||
this.showHiddenMode = showHidden;
|
||||
|
||||
// check if search is active
|
||||
let searchIsActive = false;
|
||||
if (newSeachTerm && newSeachTerm.trim().length > 0) {
|
||||
searchIsActive = true;
|
||||
}
|
||||
|
||||
// check if search is hit
|
||||
const hasHits = this.items.length > 0;
|
||||
|
||||
// action if search is active and search hits and it was collapsed
|
||||
if (searchIsActive && hasHits && this.isCollapsed) {
|
||||
this.expandCollapseSection();
|
||||
this.autoOpenedBySearch = true;
|
||||
}
|
||||
|
||||
// action if that search is not a hit anymore
|
||||
if (this.autoOpenedBySearch && (!searchIsActive || !hasHits)) {
|
||||
this.expandCollapseSection();
|
||||
this.autoOpenedBySearch = false;
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/* Opens the iframe modal */
|
||||
|
|
@ -300,6 +364,21 @@ export default {
|
|||
const secElem = this.$refs[this.sectionRef];
|
||||
if (secElem && secElem.$el.clientWidth) this.sectionWidth = secElem.$el.clientWidth;
|
||||
},
|
||||
saveUnlockPins({ pin, id }) {
|
||||
const map = JSON.parse(localStorage.getItem(SECRET_UNLOCKED_KEY) || '{}');
|
||||
map[id] = pin;
|
||||
localStorage.setItem(SECRET_UNLOCKED_KEY, JSON.stringify(map));
|
||||
this.updateUnlocked();
|
||||
},
|
||||
updateUnlocked() {
|
||||
const unlockPins = JSON.parse(localStorage.getItem(SECRET_UNLOCKED_KEY) || '{}');
|
||||
const sectionKey = this.sectionRef;
|
||||
if (unlockPins[sectionKey] === this.pin) {
|
||||
this.isUnlocked = true;
|
||||
} else {
|
||||
this.isUnlocked = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// Set the section width, and recalculate when section resized
|
||||
|
|
@ -307,6 +386,21 @@ export default {
|
|||
this.resizeObserver = new ResizeObserver(this.calculateSectionWidth)
|
||||
.observe(this.$refs[this.sectionRef].$el);
|
||||
}
|
||||
if (this.displayData?.collapsed) {
|
||||
this.isCollapsed = this.displayData.collapsed;
|
||||
}
|
||||
if (this.displayData?.secret) {
|
||||
if (this.displayData.secret) this.isUnlocked = false;
|
||||
|
||||
const secretPin = String(this.pin || '0000');
|
||||
const sectionKey = this.sectionRef;
|
||||
|
||||
const pins = JSON.parse(localStorage.getItem(SECRET_PINS_KEY) || '{}');
|
||||
if (pins[sectionKey] !== secretPin) {
|
||||
pins[sectionKey] = secretPin;
|
||||
localStorage.setItem(SECRET_PINS_KEY, JSON.stringify(pins));
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// If resize observer set, and element still present, then de-register
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
:index="index"
|
||||
:title="section.name"
|
||||
:icon="section.icon || undefined"
|
||||
:pin="section.pin || undefined"
|
||||
:displayData="getDisplayData(section)"
|
||||
:groupId="makeSectionId(section)"
|
||||
:items="section.filteredItems"
|
||||
|
|
|
|||
|
|
@ -44,4 +44,19 @@ sections:
|
|||
description: Get help with Dashy, raise a bug, or get in contact
|
||||
url: https://github.com/Lissy93/dashy/blob/master/.github/SUPPORT.md
|
||||
icon: far fa-hands-helping
|
||||
|
||||
- name: Stuff
|
||||
icon: fas fa-rocket
|
||||
pin: "1111"
|
||||
displayData:
|
||||
secret: true
|
||||
items:
|
||||
- title: hidden tile 1
|
||||
description: Development a project management links for Dashy
|
||||
icon: https://i.ibb.co/qWWpD0v/astro-dab-128.png
|
||||
url: https://live.dashy.to/
|
||||
target: newtab
|
||||
- title: hidden tile 2
|
||||
description: Development a project management links for Dashy
|
||||
icon: https://i.ibb.co/qWWpD0v/astro-dab-128.png
|
||||
url: https://live.dashy.to/
|
||||
target: newtab
|
||||
|
|
|
|||
Loading…
Reference in a new issue