Dateien
TaskMate/frontend/js/mobile-swipe.js
Server Deploy 5c87254e97 Sicherheits-Fixes, toter Code entfernt, Optimierungen
Sicherheit:
- CSRF-Schutz auf allen API-Routes (admin, proposals, files, stats, export)
- authenticateToken vor csrfProtection bei admin/proposals (CSRF-Bypass behoben)
- CORS eingeschränkt auf taskmate.aegis-sight.de
- JWT_SECRET und SESSION_TIMEOUT nicht mehr exportiert
- Tote Auth-Funktionen entfernt (generateCsrfToken, generateToken Legacy)

Toter Code entfernt:
- 6 ungenutzte JS-Dateien (tour, dashboard, 4x contacts-*)
- 2 ungenutzte CSS-Dateien (dashboard, contacts-extended)
- backend/migrations/ Verzeichnis, knowledge.js.backup
- Doppelter bcrypt require in database.js

Optimierung:
- Request-Logging filtert statische Assets (nur /api/ wird geloggt)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 19:21:40 +01:00

274 Zeilen
7.9 KiB
JavaScript

/**
* TASKMATE - Mobile Swipe Enhancement
* ====================================
* Neue Swipe-Funktionalität für bessere mobile Navigation
*/
export function enhanceMobileSwipe(mobileManager) {
const SWIPE_THRESHOLD = 50;
const SWIPE_VELOCITY = 0.3;
// State für Column-Navigation
let currentColumnIndex = 0;
let columnCount = 0;
let isColumnSwipeEnabled = false;
// Column indicator elements
let columnIndicator = null;
/**
* Initialize column swipe for board view
*/
function initColumnSwipe() {
const boardContainer = document.querySelector('.board-container');
if (!boardContainer || mobileManager.currentView !== 'board') return;
// Create column indicator
if (!columnIndicator) {
columnIndicator = document.createElement('div');
columnIndicator.className = 'mobile-column-indicator';
columnIndicator.innerHTML = `
<div class="column-dots"></div>
<div class="column-name"></div>
`;
document.querySelector('.view-board')?.appendChild(columnIndicator);
}
updateColumnInfo();
showCurrentColumn();
}
/**
* Update column information
*/
function updateColumnInfo() {
const columns = document.querySelectorAll('.column');
columnCount = columns.length;
// Update dots
const dotsContainer = columnIndicator?.querySelector('.column-dots');
if (dotsContainer) {
dotsContainer.innerHTML = Array.from({ length: columnCount }, (_, i) =>
`<span class="dot ${i === currentColumnIndex ? 'active' : ''}"></span>`
).join('');
}
// Update column name
const nameContainer = columnIndicator?.querySelector('.column-name');
if (nameContainer && columns[currentColumnIndex]) {
const columnTitle = columns[currentColumnIndex].querySelector('.column-title')?.textContent || '';
nameContainer.textContent = columnTitle;
}
}
/**
* Show specific column (hide others)
*/
function showCurrentColumn() {
const columns = document.querySelectorAll('.column');
const boardContainer = document.querySelector('.board-container');
columns.forEach((col, index) => {
if (index === currentColumnIndex) {
col.style.display = 'flex';
col.classList.add('mobile-active');
} else {
col.style.display = 'none';
col.classList.remove('mobile-active');
}
});
// Update add column button
const addColumnBtn = document.querySelector('.btn-add-column');
if (addColumnBtn) {
addColumnBtn.style.display = currentColumnIndex === columnCount - 1 ? 'flex' : 'none';
}
updateColumnInfo();
}
/**
* Navigate to specific column
*/
function navigateToColumn(index) {
if (index < 0 || index >= columnCount) return;
currentColumnIndex = index;
showCurrentColumn();
// Haptic feedback
if (navigator.vibrate) {
navigator.vibrate(10);
}
}
/**
* Enhanced board swipe handler
*/
mobileManager.handleBoardSwipeEnd = function() {
if (!this.isSwiping || this.swipeDirection !== 'horizontal' || this.swipeTarget !== 'board') {
this.resetSwipe();
return;
}
const deltaX = this.touchCurrentX - this.touchStartX;
const deltaTime = Date.now() - this.touchStartTime;
const velocity = Math.abs(deltaX) / deltaTime;
const isValidSwipe = Math.abs(deltaX) > SWIPE_THRESHOLD || velocity > SWIPE_VELOCITY;
if (isValidSwipe && isColumnSwipeEnabled) {
if (deltaX > 0 && currentColumnIndex > 0) {
// Swipe right - previous column
navigateToColumn(currentColumnIndex - 1);
} else if (deltaX < 0 && currentColumnIndex < columnCount - 1) {
// Swipe left - next column
navigateToColumn(currentColumnIndex + 1);
}
}
this.resetSwipe();
};
/**
* View hint for header swipes
*/
let viewHint = null;
function showViewSwipeHint(viewName, direction) {
if (!viewHint) {
viewHint = document.createElement('div');
viewHint.className = 'mobile-view-hint';
document.body.appendChild(viewHint);
}
viewHint.textContent = getViewDisplayName(viewName);
viewHint.classList.add('visible', direction);
}
function hideViewSwipeHint() {
if (viewHint) {
viewHint.classList.remove('visible', 'left', 'right');
}
}
function getViewDisplayName(view) {
const names = {
'board': 'Board',
'list': 'Liste',
'calendar': 'Kalender',
'proposals': 'Genehmigungen',
'gitea': 'Gitea',
'knowledge': 'Wissen'
};
return names[view] || view;
}
/**
* Enhanced header swipe handler
*/
mobileManager.handleHeaderSwipeMove = function(e) {
if (!this.isMobile || this.touchStartX === 0 || this.swipeTarget !== 'header') return;
const touch = e.touches[0];
this.touchCurrentX = touch.clientX;
this.touchCurrentY = touch.clientY;
const deltaX = this.touchCurrentX - this.touchStartX;
const deltaY = this.touchCurrentY - this.touchStartY;
// Determine direction
if (!this.swipeDirection && (Math.abs(deltaX) > 10 || Math.abs(deltaY) > 10)) {
if (Math.abs(deltaX) > Math.abs(deltaY) * 1.5) {
this.swipeDirection = 'horizontal';
this.isSwiping = true;
} else {
this.swipeDirection = 'vertical';
this.resetSwipe();
return;
}
}
if (this.swipeDirection !== 'horizontal') return;
e.preventDefault();
// Show view hints
const currentIndex = this.viewOrder.indexOf(this.currentView);
if (deltaX > SWIPE_THRESHOLD && currentIndex > 0) {
showViewSwipeHint(this.viewOrder[currentIndex - 1], 'left');
} else if (deltaX < -SWIPE_THRESHOLD && currentIndex < this.viewOrder.length - 1) {
showViewSwipeHint(this.viewOrder[currentIndex + 1], 'right');
} else {
hideViewSwipeHint();
}
};
mobileManager.handleHeaderSwipeEnd = function() {
if (!this.isSwiping || this.swipeDirection !== 'horizontal' || this.swipeTarget !== 'header') {
this.resetSwipe();
hideViewSwipeHint();
return;
}
const deltaX = this.touchCurrentX - this.touchStartX;
const deltaTime = Date.now() - this.touchStartTime;
const velocity = Math.abs(deltaX) / deltaTime;
const isValidSwipe = Math.abs(deltaX) > SWIPE_THRESHOLD || velocity > SWIPE_VELOCITY;
if (isValidSwipe) {
const currentIndex = this.viewOrder.indexOf(this.currentView);
if (deltaX > 0 && currentIndex > 0) {
// Swipe right - previous view
this.switchView(this.viewOrder[currentIndex - 1]);
} else if (deltaX < 0 && currentIndex < this.viewOrder.length - 1) {
// Swipe left - next view
this.switchView(this.viewOrder[currentIndex + 1]);
}
}
hideViewSwipeHint();
this.resetSwipe();
};
// Listen for view changes
document.addEventListener('view:changed', (e) => {
if (e.detail?.view === 'board' && mobileManager.isMobile) {
isColumnSwipeEnabled = true;
setTimeout(initColumnSwipe, 100);
} else {
isColumnSwipeEnabled = false;
if (columnIndicator) {
columnIndicator.style.display = 'none';
}
}
});
// Listen for column updates
document.addEventListener('columns:updated', () => {
if (isColumnSwipeEnabled) {
updateColumnInfo();
showCurrentColumn();
}
});
// Update on resize
window.addEventListener('resize', () => {
if (mobileManager.isMobile && mobileManager.currentView === 'board') {
if (!isColumnSwipeEnabled) {
isColumnSwipeEnabled = true;
initColumnSwipe();
}
} else {
isColumnSwipeEnabled = false;
// Show all columns on desktop
document.querySelectorAll('.column').forEach(col => {
col.style.display = '';
col.classList.remove('mobile-active');
});
if (columnIndicator) {
columnIndicator.style.display = 'none';
}
}
});
}