Merge pull request #162 from gamosoft/features/qr-code
added QR code generation
This commit is contained in:
commit
8e899f0de2
|
|
@ -277,6 +277,7 @@ function noteApp() {
|
||||||
showShareModal: false,
|
showShareModal: false,
|
||||||
shareInfo: null,
|
shareInfo: null,
|
||||||
shareLoading: false,
|
shareLoading: false,
|
||||||
|
showShareQR: false,
|
||||||
shareLinkCopied: false,
|
shareLinkCopied: false,
|
||||||
_sharedNotePaths: new Set(), // O(1) lookup for shared note indicators
|
_sharedNotePaths: new Set(), // O(1) lookup for shared note indicators
|
||||||
|
|
||||||
|
|
@ -5378,13 +5379,40 @@ function noteApp() {
|
||||||
this.closeQuickSwitcher();
|
this.closeQuickSwitcher();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Close share modal and reset state after animation
|
||||||
|
closeShareModal() {
|
||||||
|
this.showShareModal = false;
|
||||||
|
// Delay state reset until modal is fully hidden
|
||||||
|
setTimeout(() => {
|
||||||
|
this.showShareQR = false;
|
||||||
|
this.shareInfo = null;
|
||||||
|
this.shareLoading = false;
|
||||||
|
}, 200);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Generate QR code for share URL
|
||||||
|
generateQRCode(url) {
|
||||||
|
if (!url || typeof qrcode === 'undefined') return '';
|
||||||
|
try {
|
||||||
|
const qr = qrcode(0, 'M'); // 0 = auto version, M = medium error correction
|
||||||
|
qr.addData(url);
|
||||||
|
qr.make();
|
||||||
|
return qr.createDataURL(4); // 4 = module size in pixels
|
||||||
|
} catch (e) {
|
||||||
|
console.error('QR code generation failed:', e);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Open share modal and fetch current share status
|
// Open share modal and fetch current share status
|
||||||
async openShareModal() {
|
async openShareModal() {
|
||||||
if (!this.currentNote) return;
|
if (!this.currentNote) return;
|
||||||
|
|
||||||
this.showShareModal = true;
|
// Reset state BEFORE showing modal to prevent flicker
|
||||||
this.shareLoading = true;
|
this.showShareQR = false;
|
||||||
this.shareInfo = null;
|
this.shareInfo = null;
|
||||||
|
this.shareLoading = true;
|
||||||
|
this.showShareModal = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const notePath = this.currentNote.replace('.md', '');
|
const notePath = this.currentNote.replace('.md', '');
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,8 @@
|
||||||
|
|
||||||
<!-- vis-network for graph visualization (v9.1.9) -->
|
<!-- vis-network for graph visualization (v9.1.9) -->
|
||||||
<script src="https://unpkg.com/vis-network@9.1.9/standalone/umd/vis-network.min.js"></script>
|
<script src="https://unpkg.com/vis-network@9.1.9/standalone/umd/vis-network.min.js"></script>
|
||||||
|
<!-- QR Code generator for share links -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js"></script>
|
||||||
|
|
||||||
<!-- Theme styles will be loaded dynamically -->
|
<!-- Theme styles will be loaded dynamically -->
|
||||||
<link rel="stylesheet" id="theme-stylesheet" href="">
|
<link rel="stylesheet" id="theme-stylesheet" href="">
|
||||||
|
|
@ -3007,8 +3009,8 @@
|
||||||
|
|
||||||
<!-- Share Modal -->
|
<!-- Share Modal -->
|
||||||
<div x-show="showShareModal"
|
<div x-show="showShareModal"
|
||||||
@click.self="showShareModal = false"
|
@click.self="closeShareModal()"
|
||||||
@keydown.escape.window="showShareModal && (showShareModal = false)"
|
@keydown.escape.window="showShareModal && closeShareModal()"
|
||||||
class="fixed inset-0 flex items-center justify-center"
|
class="fixed inset-0 flex items-center justify-center"
|
||||||
style="background-color: rgba(0, 0, 0, 0.5); z-index: 1100;"
|
style="background-color: rgba(0, 0, 0, 0.5); z-index: 1100;"
|
||||||
x-cloak>
|
x-cloak>
|
||||||
|
|
@ -3073,6 +3075,36 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- QR Code -->
|
||||||
|
<div class="mb-4 flex flex-col items-center">
|
||||||
|
<button
|
||||||
|
@click="showShareQR = !showShareQR"
|
||||||
|
class="text-sm flex items-center gap-1 mb-2 transition-colors"
|
||||||
|
style="color: var(--text-secondary);"
|
||||||
|
onmouseover="this.style.color='var(--text-primary)'"
|
||||||
|
onmouseout="this.style.color='var(--text-secondary)'"
|
||||||
|
>
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h.01M5 8h2a1 1 0 001-1V5a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1zm12 0h2a1 1 0 001-1V5a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 001 1zM5 20h2a1 1 0 001-1v-2a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1z"></path>
|
||||||
|
</svg>
|
||||||
|
<span x-text="showShareQR ? t('share.hide_qr') : t('share.show_qr')"></span>
|
||||||
|
<svg class="w-3 h-3 transition-transform" :class="showShareQR ? 'rotate-180' : ''" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div x-show="showShareQR"
|
||||||
|
x-transition:enter="transition ease-out duration-200"
|
||||||
|
x-transition:enter-start="opacity-0 transform scale-95"
|
||||||
|
x-transition:enter-end="opacity-100 transform scale-100"
|
||||||
|
x-transition:leave="transition ease-in duration-150"
|
||||||
|
x-transition:leave-start="opacity-100 transform scale-100"
|
||||||
|
x-transition:leave-end="opacity-0 transform scale-95"
|
||||||
|
class="p-3 rounded-lg"
|
||||||
|
style="background-color: white;">
|
||||||
|
<img :src="generateQRCode(shareInfo?.url)" alt="QR Code" class="w-32 h-32" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Revoke sharing -->
|
<!-- Revoke sharing -->
|
||||||
<button
|
<button
|
||||||
@click="revokeShareLink()"
|
@click="revokeShareLink()"
|
||||||
|
|
@ -3088,7 +3120,7 @@
|
||||||
<!-- Close button -->
|
<!-- Close button -->
|
||||||
<div class="mt-4 pt-4 border-t" style="border-color: var(--border-primary);">
|
<div class="mt-4 pt-4 border-t" style="border-color: var(--border-primary);">
|
||||||
<button
|
<button
|
||||||
@click="showShareModal = false"
|
@click="closeShareModal()"
|
||||||
class="w-full px-4 py-2 rounded font-medium transition-colors"
|
class="w-full px-4 py-2 rounded font-medium transition-colors"
|
||||||
style="background-color: var(--bg-tertiary); color: var(--text-primary);"
|
style="background-color: var(--bg-tertiary); color: var(--text-primary);"
|
||||||
onmouseover="this.style.backgroundColor='var(--bg-hover)'"
|
onmouseover="this.style.backgroundColor='var(--bg-hover)'"
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,9 @@
|
||||||
"stop_sharing": "Freigabe beenden",
|
"stop_sharing": "Freigabe beenden",
|
||||||
"confirm_revoke": "Sind Sie sicher, dass Sie die Freigabe dieser Notiz beenden möchten? Der Freigabelink funktioniert dann nicht mehr.",
|
"confirm_revoke": "Sind Sie sicher, dass Sie die Freigabe dieser Notiz beenden möchten? Der Freigabelink funktioniert dann nicht mehr.",
|
||||||
"error_creating": "Fehler beim Erstellen des Freigabelinks: {{error}}",
|
"error_creating": "Fehler beim Erstellen des Freigabelinks: {{error}}",
|
||||||
"error_revoking": "Fehler beim Widerrufen des Freigabelinks: {{error}}"
|
"error_revoking": "Fehler beim Widerrufen des Freigabelinks: {{error}}",
|
||||||
|
"show_qr": "QR-Code anzeigen",
|
||||||
|
"hide_qr": "QR-Code ausblenden"
|
||||||
},
|
},
|
||||||
|
|
||||||
"quick_switcher": {
|
"quick_switcher": {
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,9 @@
|
||||||
"stop_sharing": "Stop Sharing",
|
"stop_sharing": "Stop Sharing",
|
||||||
"confirm_revoke": "Are you sure you want to stop sharing this note? The share link will no longer work.",
|
"confirm_revoke": "Are you sure you want to stop sharing this note? The share link will no longer work.",
|
||||||
"error_creating": "Failed to create share link: {{error}}",
|
"error_creating": "Failed to create share link: {{error}}",
|
||||||
"error_revoking": "Failed to revoke share link: {{error}}"
|
"error_revoking": "Failed to revoke share link: {{error}}",
|
||||||
|
"show_qr": "Show QR Code",
|
||||||
|
"hide_qr": "Hide QR Code"
|
||||||
},
|
},
|
||||||
|
|
||||||
"quick_switcher": {
|
"quick_switcher": {
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,9 @@
|
||||||
"stop_sharing": "Stop Sharing",
|
"stop_sharing": "Stop Sharing",
|
||||||
"confirm_revoke": "Are you sure you want to stop sharing this note? The share link will no longer work.",
|
"confirm_revoke": "Are you sure you want to stop sharing this note? The share link will no longer work.",
|
||||||
"error_creating": "Failed to create share link: {{error}}",
|
"error_creating": "Failed to create share link: {{error}}",
|
||||||
"error_revoking": "Failed to revoke share link: {{error}}"
|
"error_revoking": "Failed to revoke share link: {{error}}",
|
||||||
|
"show_qr": "Show QR Code",
|
||||||
|
"hide_qr": "Hide QR Code"
|
||||||
},
|
},
|
||||||
|
|
||||||
"quick_switcher": {
|
"quick_switcher": {
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,9 @@
|
||||||
"stop_sharing": "Dejar de compartir",
|
"stop_sharing": "Dejar de compartir",
|
||||||
"confirm_revoke": "¿Estás seguro de que quieres dejar de compartir esta nota? El enlace dejará de funcionar.",
|
"confirm_revoke": "¿Estás seguro de que quieres dejar de compartir esta nota? El enlace dejará de funcionar.",
|
||||||
"error_creating": "Error al crear el enlace: {{error}}",
|
"error_creating": "Error al crear el enlace: {{error}}",
|
||||||
"error_revoking": "Error al revocar el enlace: {{error}}"
|
"error_revoking": "Error al revocar el enlace: {{error}}",
|
||||||
|
"show_qr": "Mostrar código QR",
|
||||||
|
"hide_qr": "Ocultar código QR"
|
||||||
},
|
},
|
||||||
|
|
||||||
"quick_switcher": {
|
"quick_switcher": {
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,9 @@
|
||||||
"stop_sharing": "Arrêter le partage",
|
"stop_sharing": "Arrêter le partage",
|
||||||
"confirm_revoke": "Êtes-vous sûr de vouloir arrêter le partage de cette note ? Le lien ne fonctionnera plus.",
|
"confirm_revoke": "Êtes-vous sûr de vouloir arrêter le partage de cette note ? Le lien ne fonctionnera plus.",
|
||||||
"error_creating": "Échec de la création du lien : {{error}}",
|
"error_creating": "Échec de la création du lien : {{error}}",
|
||||||
"error_revoking": "Échec de la révocation du lien : {{error}}"
|
"error_revoking": "Échec de la révocation du lien : {{error}}",
|
||||||
|
"show_qr": "Afficher le code QR",
|
||||||
|
"hide_qr": "Masquer le code QR"
|
||||||
},
|
},
|
||||||
|
|
||||||
"quick_switcher": {
|
"quick_switcher": {
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,9 @@
|
||||||
"stop_sharing": "Interrompi condivisione",
|
"stop_sharing": "Interrompi condivisione",
|
||||||
"confirm_revoke": "Sei sicuro di voler interrompere la condivisione di questa nota? Il link non funzionerà più.",
|
"confirm_revoke": "Sei sicuro di voler interrompere la condivisione di questa nota? Il link non funzionerà più.",
|
||||||
"error_creating": "Impossibile creare il link: {{error}}",
|
"error_creating": "Impossibile creare il link: {{error}}",
|
||||||
"error_revoking": "Impossibile revocare il link: {{error}}"
|
"error_revoking": "Impossibile revocare il link: {{error}}",
|
||||||
|
"show_qr": "Mostra codice QR",
|
||||||
|
"hide_qr": "Nascondi codice QR"
|
||||||
},
|
},
|
||||||
|
|
||||||
"quick_switcher": {
|
"quick_switcher": {
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,9 @@
|
||||||
"stop_sharing": "共有を停止",
|
"stop_sharing": "共有を停止",
|
||||||
"confirm_revoke": "このノートの共有を停止してもよろしいですか?共有リンクは機能しなくなります。",
|
"confirm_revoke": "このノートの共有を停止してもよろしいですか?共有リンクは機能しなくなります。",
|
||||||
"error_creating": "共有リンクの作成に失敗しました: {{error}}",
|
"error_creating": "共有リンクの作成に失敗しました: {{error}}",
|
||||||
"error_revoking": "共有リンクの取り消しに失敗しました: {{error}}"
|
"error_revoking": "共有リンクの取り消しに失敗しました: {{error}}",
|
||||||
|
"show_qr": "QRコードを表示",
|
||||||
|
"hide_qr": "QRコードを非表示"
|
||||||
},
|
},
|
||||||
|
|
||||||
"quick_switcher": {
|
"quick_switcher": {
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,9 @@
|
||||||
"stop_sharing": "Прекратить доступ",
|
"stop_sharing": "Прекратить доступ",
|
||||||
"confirm_revoke": "Вы уверены, что хотите прекратить доступ к этой заметке? Ссылка перестанет работать.",
|
"confirm_revoke": "Вы уверены, что хотите прекратить доступ к этой заметке? Ссылка перестанет работать.",
|
||||||
"error_creating": "Не удалось создать ссылку: {{error}}",
|
"error_creating": "Не удалось создать ссылку: {{error}}",
|
||||||
"error_revoking": "Не удалось отозвать ссылку: {{error}}"
|
"error_revoking": "Не удалось отозвать ссылку: {{error}}",
|
||||||
|
"show_qr": "Показать QR-код",
|
||||||
|
"hide_qr": "Скрыть QR-код"
|
||||||
},
|
},
|
||||||
|
|
||||||
"quick_switcher": {
|
"quick_switcher": {
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,9 @@
|
||||||
"stop_sharing": "Prenehaj deliti",
|
"stop_sharing": "Prenehaj deliti",
|
||||||
"confirm_revoke": "Ali ste prepričani, da želite prenehati deliti ta zapis? Povezava ne bo več delovala.",
|
"confirm_revoke": "Ali ste prepričani, da želite prenehati deliti ta zapis? Povezava ne bo več delovala.",
|
||||||
"error_creating": "Napaka pri ustvarjanju povezave: {{error}}",
|
"error_creating": "Napaka pri ustvarjanju povezave: {{error}}",
|
||||||
"error_revoking": "Napaka pri preklicu povezave: {{error}}"
|
"error_revoking": "Napaka pri preklicu povezave: {{error}}",
|
||||||
|
"show_qr": "Prikaži QR kodo",
|
||||||
|
"hide_qr": "Skrij QR kodo"
|
||||||
},
|
},
|
||||||
|
|
||||||
"quick_switcher": {
|
"quick_switcher": {
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,9 @@
|
||||||
"stop_sharing": "停止分享",
|
"stop_sharing": "停止分享",
|
||||||
"confirm_revoke": "您确定要停止分享此笔记吗?分享链接将不再有效。",
|
"confirm_revoke": "您确定要停止分享此笔记吗?分享链接将不再有效。",
|
||||||
"error_creating": "创建分享链接失败:{{error}}",
|
"error_creating": "创建分享链接失败:{{error}}",
|
||||||
"error_revoking": "撤销分享链接失败:{{error}}"
|
"error_revoking": "撤销分享链接失败:{{error}}",
|
||||||
|
"show_qr": "显示二维码",
|
||||||
|
"hide_qr": "隐藏二维码"
|
||||||
},
|
},
|
||||||
|
|
||||||
"quick_switcher": {
|
"quick_switcher": {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue