mirror of
https://github.com/bfloydd/streams.git
synced 2026-07-22 12:50:25 +00:00
276 lines
9.7 KiB
TypeScript
276 lines
9.7 KiB
TypeScript
import { App, TFile, WorkspaceLeaf, ItemView, setIcon, Notice } from 'obsidian';
|
|
import { Stream } from '../../shared/types';
|
|
import { centralizedLogger } from '../../shared/centralized-logger';
|
|
import { DateStateManager } from '../../shared/date-state-manager';
|
|
|
|
export const ENCRYPTED_FILE_VIEW_TYPE = 'streams-encrypted-file-view';
|
|
|
|
export class EncryptedFileView extends ItemView {
|
|
private filePath: string;
|
|
private stream: Stream;
|
|
private date: Date;
|
|
private dateStateManager: DateStateManager;
|
|
private unsubscribeDateChanged: (() => void) | null = null;
|
|
|
|
constructor(
|
|
leaf: WorkspaceLeaf,
|
|
app: App,
|
|
filePath: string,
|
|
stream: Stream,
|
|
date: Date
|
|
) {
|
|
super(leaf);
|
|
this.app = app;
|
|
this.filePath = filePath;
|
|
this.stream = stream;
|
|
this.date = date;
|
|
this.dateStateManager = DateStateManager.getInstance();
|
|
}
|
|
|
|
getViewType(): string {
|
|
return ENCRYPTED_FILE_VIEW_TYPE;
|
|
}
|
|
|
|
getDisplayText(): string {
|
|
try {
|
|
const dateString = this.formatTitleDate(this.date);
|
|
return `${dateString} (Encrypted)`;
|
|
} catch (error) {
|
|
centralizedLogger.error('Error formatting display text:', error);
|
|
return 'Encrypted File';
|
|
}
|
|
}
|
|
|
|
getState(): { stream: Stream; date: string; filePath: string } {
|
|
return {
|
|
filePath: this.filePath,
|
|
stream: this.stream,
|
|
date: this.date.toISOString()
|
|
};
|
|
}
|
|
|
|
async setState(state: { stream?: Stream; date?: string | Date; filePath?: string }, result?: unknown): Promise<void> {
|
|
try {
|
|
// Check if the view is still valid
|
|
if (!this || !this.contentEl || !this.leaf || this.contentEl === null || this.leaf === null) {
|
|
return;
|
|
}
|
|
|
|
// Additional safety check - ensure the view is still attached to the DOM
|
|
if (!document.contains(this.contentEl)) {
|
|
return;
|
|
}
|
|
|
|
if (state) {
|
|
this.filePath = state.filePath || this.filePath;
|
|
this.stream = state.stream || this.stream;
|
|
|
|
// Handle date parameter
|
|
if (state.date) {
|
|
const newDate = typeof state.date === 'string' ? new Date(state.date) : state.date;
|
|
if (!isNaN(newDate.getTime())) {
|
|
this.date = newDate;
|
|
}
|
|
}
|
|
|
|
// Refresh the view with new state
|
|
if (this.contentEl) {
|
|
this.contentEl.empty();
|
|
this.contentEl.addClass('streams-encrypted-file-container');
|
|
this.createEncryptedFileViewContent(this.contentEl);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
centralizedLogger.error(`Error in EncryptedFileView setState:`, error);
|
|
}
|
|
}
|
|
|
|
async onOpen(): Promise<void> {
|
|
// Set up date change listener
|
|
this.unsubscribeDateChanged = this.dateStateManager.onDateChanged((state) => {
|
|
this.handleDateChange(state);
|
|
});
|
|
|
|
// Trigger streams bar component to be added to this view
|
|
this.triggerCalendarComponent();
|
|
|
|
// Prepare our content element
|
|
this.contentEl.empty();
|
|
this.contentEl.addClass('streams-encrypted-file-container');
|
|
|
|
// Set up the content element styling
|
|
this.contentEl.style.height = '100%';
|
|
this.contentEl.style.width = '100%';
|
|
this.contentEl.style.display = 'flex';
|
|
this.contentEl.style.alignItems = 'center';
|
|
this.contentEl.style.justifyContent = 'center';
|
|
|
|
// Hide any empty-state elements that might still be present
|
|
this.hideEmptyStates();
|
|
|
|
// Create our encrypted file view content
|
|
this.createEncryptedFileViewContent(this.contentEl);
|
|
}
|
|
|
|
async onClose(): Promise<void> {
|
|
// Clean up date change listener
|
|
if (this.unsubscribeDateChanged) {
|
|
this.unsubscribeDateChanged();
|
|
this.unsubscribeDateChanged = null;
|
|
}
|
|
|
|
// Clear content and mark as invalid
|
|
if (this.contentEl) {
|
|
this.contentEl.empty();
|
|
}
|
|
|
|
// Mark the view as invalid to prevent setState calls
|
|
this.contentEl = null as any;
|
|
this.leaf = null as any;
|
|
}
|
|
|
|
private createEncryptedFileViewContent(container: HTMLElement): void {
|
|
// Create the content box
|
|
const contentBox = container.createDiv('streams-encrypted-file-content');
|
|
|
|
// Add icon
|
|
const iconContainer = contentBox.createDiv('streams-encrypted-file-icon');
|
|
setIcon(iconContainer, 'lock');
|
|
|
|
// Stream info display
|
|
const streamContainer = contentBox.createDiv('streams-encrypted-file-stream-container');
|
|
const streamIcon = streamContainer.createSpan('streams-encrypted-file-stream-icon');
|
|
setIcon(streamIcon, this.stream.icon || 'book');
|
|
|
|
const streamName = streamContainer.createSpan('streams-encrypted-file-stream');
|
|
streamName.setText(this.stream.name);
|
|
|
|
// Date display
|
|
const dateEl = contentBox.createDiv('streams-encrypted-file-date');
|
|
const formattedDate = this.formatDate(this.date);
|
|
dateEl.setText(formattedDate);
|
|
|
|
// Create button
|
|
const buttonContainer = contentBox.createDiv('streams-encrypted-file-button-container');
|
|
const createButton = buttonContainer.createEl('button', {
|
|
cls: 'mod-cta streams-encrypted-file-button',
|
|
text: 'Install Meld Encrypt Plugin'
|
|
});
|
|
|
|
|
|
createButton.addEventListener('click', () => {
|
|
this.openMeldPluginPage();
|
|
});
|
|
}
|
|
|
|
private triggerCalendarComponent(): void {
|
|
// Trigger the streams bar component to be added to this view
|
|
try {
|
|
import('../../shared/event-bus').then(({ eventBus }) => {
|
|
eventBus.emit('encrypted-file-view-opened', this.leaf);
|
|
});
|
|
} catch (error) {
|
|
// Calendar component trigger failed - not critical
|
|
}
|
|
}
|
|
|
|
private hideEmptyStates(): void {
|
|
// Check if leaf and view are still valid
|
|
if (!this.leaf || !this.leaf.view || !this.leaf.view.containerEl) {
|
|
return;
|
|
}
|
|
|
|
const hideEmptyStates = () => {
|
|
// Double-check that leaf and view are still valid
|
|
if (!this.leaf || !this.leaf.view || !this.leaf.view.containerEl) {
|
|
return;
|
|
}
|
|
|
|
const emptyStates = this.leaf.view.containerEl.querySelectorAll('.empty-state, .empty-state-container');
|
|
emptyStates.forEach(el => {
|
|
const htmlEl = el as HTMLElement;
|
|
htmlEl.style.display = 'none';
|
|
htmlEl.style.visibility = 'hidden';
|
|
htmlEl.style.opacity = '0';
|
|
htmlEl.style.height = '0';
|
|
htmlEl.style.overflow = 'hidden';
|
|
});
|
|
};
|
|
|
|
// Hide them immediately
|
|
hideEmptyStates();
|
|
|
|
// Set up a MutationObserver to hide them if they get recreated
|
|
const observer = new MutationObserver(() => {
|
|
hideEmptyStates();
|
|
});
|
|
|
|
observer.observe(this.leaf.view.containerEl, {
|
|
childList: true,
|
|
subtree: true,
|
|
attributes: false
|
|
});
|
|
|
|
// Store observer for cleanup
|
|
(this as any).emptyStateObserver = observer;
|
|
}
|
|
|
|
private formatTitleDate(date: Date): string {
|
|
return date.toLocaleDateString('en-US', {
|
|
month: 'short',
|
|
day: 'numeric',
|
|
year: 'numeric'
|
|
});
|
|
}
|
|
|
|
private handleDateChange(state: any): void {
|
|
// Update the file path based on the new date
|
|
const fileName = `${this.formatDateToYYYYMMDD(state.currentDate)}.mdenc`;
|
|
const folderPath = this.filePath.substring(0, this.filePath.lastIndexOf('/'));
|
|
this.filePath = folderPath ? `${folderPath}/${fileName}` : fileName;
|
|
|
|
// Refresh the view content
|
|
if (this.contentEl) {
|
|
this.contentEl.empty();
|
|
this.contentEl.addClass('streams-encrypted-file-container');
|
|
this.createEncryptedFileViewContent(this.contentEl);
|
|
}
|
|
}
|
|
|
|
private formatDateToYYYYMMDD(date: Date): string {
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
return `${year}-${month}-${day}`;
|
|
}
|
|
|
|
private formatDate(date: Date): string {
|
|
try {
|
|
return date.toLocaleDateString('en-US', {
|
|
weekday: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
});
|
|
} catch (error) {
|
|
centralizedLogger.error(`Error formatting date: ${error}`);
|
|
return "Invalid Date";
|
|
}
|
|
}
|
|
|
|
private openMeldPluginPage(): void {
|
|
// Open the Meld plugin page in the community plugins
|
|
try {
|
|
const setting = (this.app as any).setting;
|
|
setting.open();
|
|
setting.openTabById('community-plugins');
|
|
// Note: We can't directly search for the plugin, but we can open the community plugins tab
|
|
new Notice('Please search for "Meld Encrypt" in the Community Plugins tab');
|
|
} catch (error) {
|
|
centralizedLogger.error('Error opening Meld plugin page:', error);
|
|
new Notice('Please manually search for "Meld Encrypt" in Community Plugins');
|
|
}
|
|
}
|
|
|
|
|
|
}
|