285 lines
10 KiB
JavaScript
285 lines
10 KiB
JavaScript
// LinkSync Popup Script
|
|
// Handles bookmark management and sync operations
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
const Popup = {
|
|
// API Configuration
|
|
API_BASE_URL: '',
|
|
API_KEY: '',
|
|
|
|
// Initialize popup
|
|
async init() {
|
|
console.log('LinkSync: Popup initialized');
|
|
|
|
// Load settings
|
|
await this.loadSettings();
|
|
|
|
// Setup event listeners
|
|
this.setupEventListeners();
|
|
|
|
// Load bookmarks
|
|
await this.loadBookmarks();
|
|
|
|
// Load collections
|
|
await this.loadCollections();
|
|
|
|
// Update sync status
|
|
this.updateSyncStatus();
|
|
},
|
|
|
|
// Load settings from storage
|
|
async loadSettings() {
|
|
this.API_BASE_URL = await this.getSetting('url') || 'http://localhost:5000';
|
|
this.API_KEY = await this.getSetting('apiKey') || '';
|
|
|
|
// Update form
|
|
this.updateFormState();
|
|
},
|
|
|
|
// Get setting from storage
|
|
async getSetting(key) {
|
|
return new Promise(resolve => {
|
|
browser.storage.local.get(key, result => resolve(result[key]));
|
|
});
|
|
},
|
|
|
|
// Setup event listeners
|
|
setupEventListeners() {
|
|
// Form submission
|
|
document.getElementById('bookmark-form').addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
await this.addBookmark();
|
|
});
|
|
|
|
// Search filter
|
|
document.getElementById('search').addEventListener('input', async (e) => {
|
|
await this.filterBookmarks(e.target.value);
|
|
});
|
|
|
|
// Sync button
|
|
document.getElementById('sync-btn').addEventListener('click', async () => {
|
|
await this.syncBookmarks();
|
|
});
|
|
|
|
// Settings button
|
|
document.getElementById('settings-btn').addEventListener('click', () => {
|
|
this.openSettings();
|
|
});
|
|
},
|
|
|
|
// Update form state (edit mode)
|
|
updateFormState(isEdit = false) {
|
|
const form = document.getElementById('bookmark-form');
|
|
if (isEdit) {
|
|
form.style.display = 'block';
|
|
} else {
|
|
form.style.display = 'none';
|
|
}
|
|
},
|
|
|
|
// Load bookmarks from server
|
|
async loadBookmarks() {
|
|
try {
|
|
const response = await fetch(`${this.API_BASE_URL}/api/links/`, {
|
|
headers: { 'Authorization': `Token ${this.API_KEY}` }
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
this.renderBookmarks(data.links || []);
|
|
}
|
|
} catch (error) {
|
|
console.error('LinkSync: Failed to load bookmarks:', error);
|
|
this.renderError('Unable to connect to server. Check your settings.');
|
|
}
|
|
},
|
|
|
|
// Render bookmarks to list
|
|
renderBookmarks(bookmarks) {
|
|
const container = document.getElementById('bookmarks-container');
|
|
container.innerHTML = '';
|
|
|
|
if (!bookmarks || bookmarks.length === 0) {
|
|
container.innerHTML = '<p style="color: var(--secondary); font-size: 12px;">No bookmarks</p>';
|
|
return;
|
|
}
|
|
|
|
bookmarks.forEach(bookmark => {
|
|
const item = document.createElement('div');
|
|
item.className = 'bookmark-item';
|
|
item.innerHTML = `
|
|
<a href="${bookmark.url}" target="_blank">${bookmark.url}</a>
|
|
<div class="title">${bookmark.title}</div>
|
|
${bookmark.description ? `<div class="description">${bookmark.description}</div>` : ''}
|
|
${bookmark.tags && bookmark.tags.length > 0 ? `<div class="tags">${bookmark.tags.join(', ')}</div>` : ''}
|
|
`;
|
|
container.appendChild(item);
|
|
});
|
|
},
|
|
|
|
// Filter bookmarks by search term
|
|
async filterBookmarks(query) {
|
|
const bookmarks = await this.loadBookmarks();
|
|
const filtered = bookmarks.filter(b =>
|
|
b.title.toLowerCase().includes(query.toLowerCase()) ||
|
|
b.url.toLowerCase().includes(query.toLowerCase()) ||
|
|
(b.description && b.description.toLowerCase().includes(query.toLowerCase()))
|
|
);
|
|
this.renderBookmarks(filtered);
|
|
},
|
|
|
|
// Add bookmark
|
|
async addBookmark() {
|
|
const form = document.getElementById('bookmark-form');
|
|
const data = {
|
|
url: document.getElementById('url').value,
|
|
title: document.getElementById('title').value,
|
|
description: document.getElementById('description').value,
|
|
notes: document.getElementById('notes').value,
|
|
tags: this.formatTags(document.getElementById('tags').value),
|
|
path: document.getElementById('folder').value
|
|
};
|
|
|
|
const response = await fetch(`${this.API_BASE_URL}/api/links/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Token ${this.API_KEY}`
|
|
},
|
|
body: JSON.stringify(data)
|
|
});
|
|
|
|
if (response.ok) {
|
|
form.reset();
|
|
await this.loadBookmarks();
|
|
this.showNotification('Bookmark added', 'success');
|
|
} else {
|
|
this.showNotification('Failed to add bookmark', 'error');
|
|
}
|
|
},
|
|
|
|
// Format tags
|
|
formatTags(tagString) {
|
|
if (!tagString) return [];
|
|
return tagString.split(',').map(t => t.trim()).filter(t => t.length > 0);
|
|
},
|
|
|
|
// Load collections
|
|
async loadCollections() {
|
|
try {
|
|
const response = await fetch(`${this.API_BASE_URL}/api/collections/`, {
|
|
headers: { 'Authorization': `Token ${this.API_KEY}` }
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
this.renderCollections(data.collections || []);
|
|
}
|
|
} catch (error) {
|
|
console.error('LinkSync: Failed to load collections:', error);
|
|
}
|
|
},
|
|
|
|
// Render collections
|
|
renderCollections(collections) {
|
|
const container = document.getElementById('collections-list');
|
|
container.innerHTML = '';
|
|
|
|
if (!collections || collections.length === 0) {
|
|
container.innerHTML = '<p style="color: var(--secondary); font-size: 12px;">No collections</p>';
|
|
return;
|
|
}
|
|
|
|
collections.forEach(collection => {
|
|
const item = document.createElement('div');
|
|
item.className = 'collection-item';
|
|
item.innerHTML = `
|
|
<h3>${collection.name}</h3>
|
|
<p>${collection.description || ''}</p>
|
|
<p style="font-size: 10px; color: var(--secondary);">Type: ${collection.query_type || 'dynamic'}</p>
|
|
`;
|
|
container.appendChild(item);
|
|
});
|
|
},
|
|
|
|
// Sync bookmarks
|
|
async syncBookmarks() {
|
|
const indicator = document.getElementById('sync-indicator');
|
|
indicator.className = 'syncing';
|
|
|
|
try {
|
|
const response = await fetch(`${this.API_BASE_URL}/api/sync/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': `Token ${this.API_KEY}`
|
|
},
|
|
body: JSON.stringify({
|
|
bookmarks: [],
|
|
mode: await this.getSetting('mode') || 'bi-directional',
|
|
deletions: await this.getSetting('deletions') || false
|
|
})
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
indicator.className = 'synced';
|
|
document.getElementById('last-sync').textContent = `Last sync: ${new Date().toLocaleTimeString()}`;
|
|
this.showNotification('Sync completed', 'success');
|
|
} else {
|
|
this.showNotification('Sync failed', 'error');
|
|
}
|
|
} catch (error) {
|
|
console.error('LinkSync: Sync error:', error);
|
|
this.showNotification('Sync error', 'error');
|
|
} finally {
|
|
setTimeout(() => indicator.className = '', 2000);
|
|
}
|
|
},
|
|
|
|
// Update sync status
|
|
updateSyncStatus() {
|
|
const indicator = document.getElementById('sync-indicator');
|
|
const lastSync = document.getElementById('last-sync');
|
|
|
|
const lastSyncTime = new Date(await this.getSetting('lastSync') || Date.now());
|
|
const minutesAgo = Math.floor((Date.now() - lastSyncTime.getTime()) / 60000);
|
|
|
|
if (minutesAgo < 5) {
|
|
indicator.className = 'synced';
|
|
lastSync.textContent = `Synced ${minutesAgo} min ago`;
|
|
} else {
|
|
indicator.className = 'error';
|
|
lastSync.textContent = `Last sync: ${lastSyncTime.toLocaleString()}`;
|
|
}
|
|
},
|
|
|
|
// Open settings modal
|
|
openSettings() {
|
|
// TODO: Open settings modal
|
|
console.log('Open settings');
|
|
},
|
|
|
|
// Show notification
|
|
showNotification(message, type) {
|
|
// TODO: Show toast notification
|
|
console.log(`[LinkSync] ${message}`);
|
|
},
|
|
|
|
// Get setting
|
|
async getSetting(key) {
|
|
return new Promise(resolve => {
|
|
browser.storage.local.get(key, result => resolve(result[key]));
|
|
});
|
|
}
|
|
};
|
|
|
|
// Initialize when page loads
|
|
window.addEventListener('load', () => Popup.init());
|
|
|
|
// Expose to window
|
|
window.Popup = Popup;
|
|
|
|
})(); |