Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions docs/product-specs/group-folder-paths.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Group Folder Paths

**Status:** Shipped.

## User Story

> As a user with an established collection-based vault layout, I want to
> choose whether Raindrop sidebar Groups add another folder level so imports
> continue using the structure I expect.

## Acceptance Criteria

- [x] Group folders remain enabled by default to preserve the current layout.
- [x] Users can disable Group folders in Make It Rain's settings without
leaving Obsidian.
- [x] When enabled, a note uses `Group/Collection/Subcollection` folders.
- [x] When disabled, a note uses `Collection/Subcollection` folders.
- [x] `collectionGroup` remains available in templates and frontmatter in both
modes.
- [x] The setting explains the effect on existing pre-v1.10 layouts.

## Out of Scope

- Moving or deleting notes previously imported under another hierarchy mode.
- Configuring hierarchy behavior separately for individual imports or presets.
1 change: 1 addition & 0 deletions docs/product-specs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
| [Safe sync](safe-sync.md) | Shipped (Issue #9) | Detect remote deletions and reconcile against the local vault with human review |
| [Per-content-type templates](per-content-type-templates.md) | Shipped | Configure Markdown output per Raindrop content type (link, article, image, video, document, audio, book) |
| [Folder notes](folder-notes.md) | Shipped | Auto-generate index notes for each collection folder |
| [Group folder paths](group-folder-paths.md) | Shipped | Choose whether Raindrop Groups add a root folder above the collection hierarchy |
| [Binary attachment download](binary-attachment-download.md) | Shipped | Download native Raindrop file attachments (PDF, EPUB, image, video, audio) |
| [Import presets](import-presets.md) | Shipped | Save named bulk-import configurations and re-run them from the modal or command palette |

Expand Down
6 changes: 6 additions & 0 deletions docs/user-guide/collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ Vault Root/
└── Note 1.md
```

This is controlled by **Settings → Make It Rain → Import & Organization →
Include Raindrop group in folder path**. It is enabled by default to preserve
the current layout. Disable it to use the pre-v1.10 Collection-only layout for
future imports. The `collectionGroup` template variable and frontmatter field
remain available when Group folders are disabled.
Comment thread
frostmute marked this conversation as resolved.

### Hierarchy Options

- **Flat Structure**: All notes in one folder
Expand Down
4 changes: 4 additions & 0 deletions docs/user-guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ You can create specific templates for different types of content.

- **API Token Security**: Your API token gives access to your Raindrop.io account. Never share it with others.
- **Folder Structure**: Choose a default vault location that fits your knowledge management system.
- **Group Folder Paths**: Keep **Include Raindrop group in folder path**
enabled to mirror the Raindrop sidebar as `Group/Collection`. Disable it to
restore the pre-v1.10 `Collection`-only layout. This changes future import
paths only; `collectionGroup` metadata remains available.
- **Filename Conflicts**: If you encounter filename conflicts, consider using unique identifiers in your filename template, such as `{{id}}-{{title}}`.
- **Template Testing**: After creating custom templates, test them with a small batch of raindrops before importing your entire collection.

Expand Down
2 changes: 1 addition & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ export default class RaindropToObsidian extends Plugin implements IRaindropToObs
}
}

if (groupTitle) {
if (groupTitle && this.settings.includeGroupInFolderPath) {
pathSegments.unshift(sanitizeFileName(groupTitle));
}

Expand Down
12 changes: 12 additions & 0 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ export const DEFAULT_SETTINGS: MakeItRainSettings = {
},
downloadFiles: false,
createFolderNotes: false,
includeGroupInFolderPath: true,
namedTemplates: {
base: `---
title: "{{title}}"
Expand Down Expand Up @@ -511,6 +512,17 @@ export class RaindropToObsidianSettingTab extends PluginSettingTab {
});
});

new Setting(orgContent)
.setName('Include Raindrop group in folder path')
.setDesc('When enabled, imports use Group/Collection folders to mirror the Raindrop sidebar. Disable this to use the pre-v1.10 Collection-only layout. This affects future imports and does not move existing notes. The collectionGroup template variable and frontmatter metadata remain available either way.')
.addToggle((toggle: ToggleComponent) => {
toggle.setValue(this.plugin.settings.includeGroupInFolderPath)
.onChange(async (value: boolean) => {
this.plugin.settings.includeGroupInFolderPath = value;
await this.plugin.saveSettings();
});
});

// --- 3. Content Enhancements ---
const contentSection = containerEl.createEl('details', { cls: 'make-it-rain-settings-section' });
contentSection.createEl('summary', { text: 'Content Enhancements', cls: 'make-it-rain-section-summary' });
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export interface MakeItRainSettings {
contentTypeTemplateToggles: ContentTypeToggles;
downloadFiles: boolean;
createFolderNotes: boolean;
includeGroupInFolderPath: boolean;
archiveScraping: boolean;
namedTemplates: Record<string, string>;
enableSafeSync: boolean;
Expand Down
67 changes: 66 additions & 1 deletion tests/unit/groupHierarchy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ describe('Group Hierarchy Support', () => {
jest.clearAllMocks();
});

it('should prepend Group name to collection path and target folder', async () => {
it('should prepend Group name to collection path and target folder by default', async () => {
expect(plugin.settings.includeGroupInFolderPath).toBe(true);

// Mock data
const collections: RaindropCollection[] = [
{ _id: 10, title: 'Parent Collection' },
Expand Down Expand Up @@ -107,6 +109,69 @@ describe('Group Hierarchy Support', () => {
);
});

it('should keep Group metadata without adding a Group folder when disabled', async () => {
plugin.settings.includeGroupInFolderPath = false;

const collections: RaindropCollection[] = [
{ _id: 10, title: 'Parent Collection' },
{ _id: 20, title: 'Child Collection', parent: { $id: 10 } }
];
const raindrop: RaindropItem = {
_id: 123,
title: 'Test Bookmark',
link: 'https://example.com',
type: 'link',
collection: { $id: 20, title: 'Child Collection' },
created: '2024-01-01T00:00:00Z',
lastUpdate: '2024-01-01T00:00:00Z'
};
const options: ModalFetchOptions = {
collections: '20',
apiFilterTags: '',
includeSubcollections: false,
appendTagsToNotes: '',
useRaindropTitleForFileName: true,
tagMatchType: 'all',
filterType: 'all',
fetchOnlyNew: false,
updateExisting: true,
useDefaultTemplate: false,
overrideTemplates: false
};
const collectionIdToNameMap = new Map<number, string>([
[10, 'Parent Collection'],
[20, 'Child Collection']
]);
const collectionToGroupMap = new Map<number, string>([[10, 'MY GROUP']]);
const mockNotice = { setMessage: jest.fn(), hide: jest.fn() } as unknown as Notice;
const createSpy = jest.spyOn(plugin.app.vault, 'create').mockResolvedValue({} as any);

await plugin.processRaindrops(
[raindrop],
'Raindrops',
'',
mockNotice,
options,
{ result: true, items: collections },
collectionIdToNameMap,
new Set<string>(),
collectionToGroupMap
);

expect(createSpy).toHaveBeenCalledWith(
'Raindrops/Parent Collection/Child Collection/Test Bookmark.md',
expect.stringContaining('collectionGroup: "MY GROUP"')
);
expect(createSpy).toHaveBeenCalledWith(
expect.any(String),
expect.stringContaining('collectionPath: "Parent Collection/Child Collection"')
);
expect(createSpy).not.toHaveBeenCalledWith(
expect.stringContaining('MY GROUP/Parent Collection'),
expect.any(String)
);
});

it('should include collectionGroup in manual frontmatter when templates are disabled', async () => {
plugin.settings.isTemplateSystemEnabled = false;

Expand Down
2 changes: 2 additions & 0 deletions tests/unit/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ describe('RaindropToObsidian', () => {
it('should initialize with default settings', () => {
expect(plugin.settings).toBeDefined();
expect(plugin.settings.fileNameTemplate).toBe('{{title}}');
expect(plugin.settings.includeGroupInFolderPath).toBe(true);
});

describe('onload', () => {
Expand Down Expand Up @@ -134,6 +135,7 @@ describe('RaindropToObsidian', () => {
expect(plugin.settings.apiToken).toBe('test-token');
expect(plugin.settings.showRibbonIcon).toBe(false);
expect(plugin.settings.fileNameTemplate).toBe('{{title}}'); // Default preserved
expect(plugin.settings.includeGroupInFolderPath).toBe(true); // Added setting defaults on for existing data
expect(saveSettingsSpy).toHaveBeenCalled();
});
});
Expand Down
4 changes: 4 additions & 0 deletions tests/unit/settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ describe('RaindropToObsidianSettingTab', () => {
expect(container.classList.contains('make-it-rain-settings-container')).toBe(true);
expect(container.innerHTML).toContain('Connection &amp; Core Setup');
expect(container.innerHTML).toContain('Import &amp; Organization');
expect(container.innerHTML).toContain('Include Raindrop group in folder path');
expect(container.innerHTML).toContain('pre-v1.10 Collection-only layout');
expect(container.innerHTML).toContain('affects future imports and does not move existing notes');
expect(container.innerHTML).toContain('collectionGroup template variable and frontmatter metadata remain available');
expect(container.innerHTML).toContain('Template Engine');
});

Expand Down
Loading