mirror of
https://github.com/bfloydd/streams.git
synced 2026-07-22 05:49:02 +00:00
independent pane streams
This commit is contained in:
parent
328376520d
commit
21fe18209c
8 changed files with 76 additions and 54 deletions
BIN
file_list.txt
Normal file
BIN
file_list.txt
Normal file
Binary file not shown.
14
main.ts
14
main.ts
|
|
@ -14,7 +14,7 @@ export default class StreamsPlugin extends Plugin implements StreamsAPI {
|
|||
|
||||
async onload() {
|
||||
sliceContainer.setPlugin(this);
|
||||
|
||||
|
||||
await this.loadSettings();
|
||||
|
||||
ServiceLoader.registerAllServices();
|
||||
|
|
@ -26,7 +26,7 @@ export default class StreamsPlugin extends Plugin implements StreamsAPI {
|
|||
// Register all views using the centralized configuration
|
||||
// Note: Views are registered after services to avoid conflicts with CalendarNavigationService
|
||||
this.registerViews();
|
||||
|
||||
|
||||
this.log?.info('Streams plugin loaded with vertical slice architecture');
|
||||
}
|
||||
|
||||
|
|
@ -38,13 +38,13 @@ export default class StreamsPlugin extends Plugin implements StreamsAPI {
|
|||
async loadSettings() {
|
||||
const loadedData = await this.loadData();
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, loadedData);
|
||||
|
||||
|
||||
// Migration: ensure barStyle exists
|
||||
if (!this.settings.barStyle) {
|
||||
this.settings.barStyle = 'default';
|
||||
await this.saveSettings();
|
||||
}
|
||||
|
||||
|
||||
// Migration: ensure encryptThisStream exists for existing streams
|
||||
let needsSave = false;
|
||||
for (const stream of this.settings.streams) {
|
||||
|
|
@ -57,7 +57,7 @@ export default class StreamsPlugin extends Plugin implements StreamsAPI {
|
|||
needsSave = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (needsSave) {
|
||||
await this.saveSettings();
|
||||
}
|
||||
|
|
@ -93,8 +93,8 @@ export default class StreamsPlugin extends Plugin implements StreamsAPI {
|
|||
// ============================================================================
|
||||
|
||||
// Stream Management
|
||||
async setActiveStream(streamId: string, force?: boolean): Promise<void> {
|
||||
await serviceRegistry.streamManagement?.setActiveStream(streamId, force);
|
||||
async setActiveStream(streamId: string, force?: boolean, suppressEvent?: boolean): Promise<void> {
|
||||
await serviceRegistry.streamManagement?.setActiveStream(streamId, force, suppressEvent);
|
||||
}
|
||||
|
||||
getActiveStream(): Stream | null {
|
||||
|
|
|
|||
|
|
@ -91,18 +91,18 @@ export class ComponentLifecycleManager {
|
|||
if (component && typeof component.updateReuseCurrentTab === 'function') {
|
||||
component.updateReuseCurrentTab(settings.reuseCurrentTab);
|
||||
}
|
||||
|
||||
|
||||
// Refresh bar style for all existing components
|
||||
if (component && typeof component.refreshBarStyle === 'function') {
|
||||
component.refreshBarStyle();
|
||||
}
|
||||
|
||||
|
||||
// Update streams list for all existing components
|
||||
if (component && typeof component.updateStreamsList === 'function' && settings.streams) {
|
||||
component.updateStreamsList(settings.streams);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Force immediate refresh for mobile devices
|
||||
// Use requestAnimationFrame to ensure DOM updates are processed
|
||||
requestAnimationFrame(() => {
|
||||
|
|
@ -140,6 +140,18 @@ export class ComponentLifecycleManager {
|
|||
return !!existingComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get component for a specific leaf
|
||||
*/
|
||||
getComponentForLeaf(leaf: WorkspaceLeaf): StreamsBarComponent | undefined {
|
||||
for (const component of this.calendarComponents.values()) {
|
||||
if (component.leaf === leaf) {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stream to use for a component (active stream or default)
|
||||
*/
|
||||
|
|
@ -157,9 +169,18 @@ export class ComponentLifecycleManager {
|
|||
if (apiService) {
|
||||
const stream = apiService.getStreamForFile(filePath);
|
||||
if (stream) {
|
||||
// This is a stream file, update the stream bar to reflect the file's stream and date
|
||||
await apiService.updateStreamBarFromFile(filePath);
|
||||
centralizedLogger.debug(`Updated stream bar for file: ${filePath}`);
|
||||
// Find component for this leaf
|
||||
const component = this.getComponentForLeaf(activeLeaf);
|
||||
if (component) {
|
||||
// Update component locally
|
||||
component.updateActiveStream(stream);
|
||||
|
||||
// Update global settings silently to ensure persistence without affecting other panels
|
||||
// We use serviceRegistry.streamManagement directly because StreamsAPI doesn't expose suppressEvent
|
||||
await serviceRegistry.streamManagement?.setActiveStream(stream.id, true, true);
|
||||
|
||||
centralizedLogger.debug(`Updated stream bar for file: ${filePath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,12 +38,6 @@ export class ComponentStateManager {
|
|||
* Get the display name for the active stream
|
||||
*/
|
||||
getDisplayStreamName(): string {
|
||||
if (this.plugin?.settings?.activeStreamId) {
|
||||
const activeStream = this.streams.find(s => s.id === this.plugin!.settings.activeStreamId);
|
||||
if (activeStream) {
|
||||
return activeStream.name;
|
||||
}
|
||||
}
|
||||
return this.selectedStream.name;
|
||||
}
|
||||
|
||||
|
|
@ -51,16 +45,13 @@ export class ComponentStateManager {
|
|||
* Get the active stream ID
|
||||
*/
|
||||
getActiveStreamId(): string {
|
||||
return this.plugin?.settings?.activeStreamId || this.selectedStream.id;
|
||||
return this.selectedStream.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the active stream
|
||||
*/
|
||||
getActiveStream(): Stream {
|
||||
if (this.plugin?.settings?.activeStreamId) {
|
||||
return this.streams.find(s => s.id === this.plugin!.settings.activeStreamId) || this.selectedStream;
|
||||
}
|
||||
return this.selectedStream;
|
||||
}
|
||||
|
||||
|
|
@ -69,13 +60,13 @@ export class ComponentStateManager {
|
|||
*/
|
||||
updateStreamEncryptionIcon(container: HTMLElement): void {
|
||||
const activeStream = this.getActiveStream();
|
||||
|
||||
|
||||
// Remove existing encryption icon if it exists
|
||||
const existingIcon = container.querySelector('.streams-bar-encryption-icon');
|
||||
if (existingIcon) {
|
||||
existingIcon.remove();
|
||||
}
|
||||
|
||||
|
||||
// Add encryption icon if stream is encrypted
|
||||
if (activeStream.encryptThisStream) {
|
||||
const encryptionIcon = container.createDiv('streams-bar-encryption-icon');
|
||||
|
|
@ -92,12 +83,12 @@ export class ComponentStateManager {
|
|||
if (!this.plugin?.settings) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const barStyle = this.plugin.settings.barStyle;
|
||||
|
||||
|
||||
// Remove existing style classes
|
||||
component.removeClass('modern-style');
|
||||
|
||||
|
||||
// Apply the appropriate style class
|
||||
if (barStyle === 'modern') {
|
||||
component.addClass('modern-style');
|
||||
|
|
@ -112,11 +103,11 @@ export class ComponentStateManager {
|
|||
const todayYear = today.getFullYear();
|
||||
const todayMonth = today.getMonth();
|
||||
const todayDay = today.getDate();
|
||||
|
||||
|
||||
const currentYear = currentDate.getFullYear();
|
||||
const currentMonth = currentDate.getMonth();
|
||||
const currentDay = currentDate.getDate();
|
||||
|
||||
|
||||
if (currentYear === todayYear && currentMonth === todayMonth && currentDay === todayDay) {
|
||||
return 'TODAY';
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ export interface ComponentCallbacks {
|
|||
hideStreamsDropdown: () => void;
|
||||
updateTodayButton: () => void;
|
||||
isExpanded: () => boolean;
|
||||
onStreamSelected: (stream: Stream) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -102,10 +103,10 @@ export class ComponentUIBuilder {
|
|||
this.setupExpandedViewScroll(expandedView);
|
||||
|
||||
const navControls = collapsedView.createDiv('streams-bar-nav-controls');
|
||||
const { todayButton, streamSelector, streamsDropdown, changeStreamSection, changeStreamText } =
|
||||
const { todayButton, streamSelector, streamsDropdown, changeStreamSection, changeStreamText } =
|
||||
this.setupCollapsedView(collapsedView, navControls);
|
||||
|
||||
const { prevButton, nextButton, dateDisplay, grid, calendarRenderer, currentMonthView } =
|
||||
const { prevButton, nextButton, dateDisplay, grid, calendarRenderer, currentMonthView } =
|
||||
this.setupExpandedView(expandedView);
|
||||
|
||||
const touchGestureHandler = this.setupCalendarHandlers(
|
||||
|
|
@ -193,7 +194,7 @@ export class ComponentUIBuilder {
|
|||
this.app,
|
||||
this.settingsManager as any,
|
||||
this.reuseCurrentTab,
|
||||
() => {}
|
||||
this.callbacks.onStreamSelected
|
||||
);
|
||||
streamSelector.populateStreamsDropdown();
|
||||
|
||||
|
|
@ -306,7 +307,7 @@ export class ComponentUIBuilder {
|
|||
this.callbacks.toggleExpanded();
|
||||
}
|
||||
},
|
||||
() => {}
|
||||
() => { }
|
||||
);
|
||||
calendarRenderer.updateCalendarGrid();
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ interface PluginInterface {
|
|||
settings: {
|
||||
activeStreamId?: string;
|
||||
};
|
||||
setActiveStream(streamId: string, force?: boolean): Promise<void>;
|
||||
setActiveStream(streamId: string, force?: boolean, suppressEvent?: boolean): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -54,25 +54,25 @@ export class StreamSelector extends Component {
|
|||
*/
|
||||
populateStreamsDropdown(): void {
|
||||
if (!this.dropdown) return;
|
||||
|
||||
|
||||
this.dropdown.empty();
|
||||
|
||||
|
||||
// Filter out disabled streams
|
||||
const enabledStreams = this.streams.filter(stream => !stream.disabled);
|
||||
|
||||
|
||||
enabledStreams.forEach(stream => {
|
||||
const streamItem = this.dropdown.createDiv('streams-bar-stream-item');
|
||||
|
||||
|
||||
const isSelected = stream.id === this.activeStreamId;
|
||||
if (isSelected) {
|
||||
streamItem.addClass('streams-bar-stream-item-selected');
|
||||
}
|
||||
|
||||
|
||||
const streamIcon = streamItem.createDiv('streams-bar-stream-item-icon');
|
||||
setIcon(streamIcon, stream.icon);
|
||||
const streamName = streamItem.createDiv('streams-bar-stream-item-name');
|
||||
streamName.setText(stream.name);
|
||||
|
||||
|
||||
// Add encryption icon if stream is encrypted
|
||||
if (stream.encryptThisStream) {
|
||||
const encryptionIcon = streamItem.createDiv('streams-bar-stream-item-encryption');
|
||||
|
|
@ -80,18 +80,18 @@ export class StreamSelector extends Component {
|
|||
encryptionIcon.setAttribute('title', 'Encrypted stream');
|
||||
encryptionIcon.setAttribute('aria-label', 'Encrypted stream');
|
||||
}
|
||||
|
||||
|
||||
if (isSelected) {
|
||||
const checkmark = streamItem.createDiv('streams-bar-stream-item-checkmark');
|
||||
setIcon(checkmark, 'check');
|
||||
}
|
||||
|
||||
|
||||
streamItem.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
this.selectStream(stream);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Force a reflow on mobile devices to ensure the dropdown updates immediately
|
||||
if (this.dropdown) {
|
||||
// Trigger a reflow to ensure the changes are visible
|
||||
|
|
@ -105,7 +105,7 @@ export class StreamSelector extends Component {
|
|||
updateStreams(streams: Stream[]): void {
|
||||
this.streams = streams;
|
||||
this.populateStreamsDropdown();
|
||||
|
||||
|
||||
// Force immediate DOM update for mobile devices
|
||||
requestAnimationFrame(() => {
|
||||
this.dropdown?.offsetHeight;
|
||||
|
|
@ -126,7 +126,7 @@ export class StreamSelector extends Component {
|
|||
private async selectStream(stream: Stream): Promise<void> {
|
||||
// Update the plugin's active stream - this will trigger the event listener
|
||||
if (this.plugin) {
|
||||
await this.plugin.setActiveStream(stream.id, true);
|
||||
await this.plugin.setActiveStream(stream.id, true, true);
|
||||
}
|
||||
|
||||
// Notify parent component
|
||||
|
|
@ -147,7 +147,7 @@ export class StreamSelector extends Component {
|
|||
try {
|
||||
const state = this.dateStateManager.getState();
|
||||
const targetDate = state.currentDate;
|
||||
|
||||
|
||||
const command = new OpenStreamDateCommand(this.app, stream, targetDate, this.reuseCurrentTab);
|
||||
await command.execute();
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -161,6 +161,9 @@ export class StreamsBarComponent extends Component {
|
|||
},
|
||||
isExpanded: () => {
|
||||
return this.expanded;
|
||||
},
|
||||
onStreamSelected: (stream: Stream) => {
|
||||
this.updateActiveStream(stream);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -399,6 +402,10 @@ export class StreamsBarComponent extends Component {
|
|||
return;
|
||||
}
|
||||
|
||||
this.updateActiveStream(newActiveStream);
|
||||
}
|
||||
|
||||
public updateActiveStream(newActiveStream: Stream): void {
|
||||
this.selectedStream = newActiveStream;
|
||||
this.stateManager.updateSelectedStream(newActiveStream);
|
||||
this.dateNavigationService.updateStream(newActiveStream);
|
||||
|
|
@ -419,7 +426,7 @@ export class StreamsBarComponent extends Component {
|
|||
}
|
||||
|
||||
if (this.streamSelector) {
|
||||
this.streamSelector.updateActiveStreamId(streamId);
|
||||
this.streamSelector.updateActiveStreamId(newActiveStream.id);
|
||||
}
|
||||
|
||||
this.updateTodayButton();
|
||||
|
|
|
|||
|
|
@ -55,9 +55,9 @@ export class StreamManagementService extends SettingsAwareSliceService {
|
|||
/**
|
||||
* Set the active stream
|
||||
*/
|
||||
public setActiveStream = withAsyncErrorHandling(async (streamId: string | undefined, force = false): Promise<void> => {
|
||||
public setActiveStream = withAsyncErrorHandling(async (streamId: string | undefined, force = false, suppressEvent = false): Promise<void> => {
|
||||
const currentActiveStreamId = this.getSettings().activeStreamId;
|
||||
|
||||
|
||||
if (currentActiveStreamId === streamId && !force) {
|
||||
return; // No change needed
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ export class StreamManagementService extends SettingsAwareSliceService {
|
|||
// Update the settings
|
||||
const plugin = this.getPlugin();
|
||||
plugin.settings.activeStreamId = streamId;
|
||||
|
||||
|
||||
// Save settings
|
||||
await plugin.saveSettings();
|
||||
|
||||
|
|
@ -81,7 +81,9 @@ export class StreamManagementService extends SettingsAwareSliceService {
|
|||
this.globalIndicator.update(this.getActiveStream());
|
||||
|
||||
// Emit event for other services
|
||||
eventBus.emit(EVENTS.ACTIVE_STREAM_CHANGED, { streamId, previousStreamId: currentActiveStreamId }, 'stream-management');
|
||||
if (!suppressEvent) {
|
||||
eventBus.emit(EVENTS.ACTIVE_STREAM_CHANGED, { streamId, previousStreamId: currentActiveStreamId }, 'stream-management');
|
||||
}
|
||||
}, 'stream-management', 'setActiveStream');
|
||||
|
||||
/**
|
||||
|
|
@ -96,8 +98,8 @@ export class StreamManagementService extends SettingsAwareSliceService {
|
|||
*/
|
||||
public showStreamSelection = withErrorHandling((): void => {
|
||||
const modal = new StreamSelectionModal(
|
||||
this.getPlugin().app,
|
||||
this.getStreams(),
|
||||
this.getPlugin().app,
|
||||
this.getStreams(),
|
||||
async (selectedStream) => {
|
||||
if (selectedStream) {
|
||||
await this.setActiveStream(selectedStream.id, true);
|
||||
|
|
|
|||
Loading…
Reference in a new issue