mirror of
https://github.com/jvsteiner/send-note.git
synced 2026-07-22 11:20:26 +00:00
339 lines
11 KiB
TypeScript
339 lines
11 KiB
TypeScript
import { App, PluginSettingTab, Setting, TextComponent, requestUrl, setIcon } from "obsidian";
|
|
import SendNotePlugin from "./main";
|
|
import StatusMessage, { StatusType } from "./StatusMessage";
|
|
|
|
export enum ThemeMode {
|
|
"Same as theme",
|
|
Dark,
|
|
Light,
|
|
}
|
|
|
|
export enum TitleSource {
|
|
"Note title",
|
|
"First H1",
|
|
"Frontmatter property",
|
|
}
|
|
|
|
export enum YamlField {
|
|
link,
|
|
updated,
|
|
encrypted,
|
|
unencrypted,
|
|
title,
|
|
expires,
|
|
}
|
|
|
|
export interface SendNoteSettings {
|
|
yamlField: string;
|
|
titleSource: TitleSource;
|
|
removeYaml: boolean;
|
|
clipboard: boolean;
|
|
shareUnencrypted: boolean;
|
|
debug: number;
|
|
accessKey: string;
|
|
secretKey: string;
|
|
region: string;
|
|
bucket: string;
|
|
folder: string;
|
|
expiry: string;
|
|
imageUrlPath: string;
|
|
useCustomEndpoint: boolean;
|
|
customEndpoint: string;
|
|
forcePathStyle: boolean;
|
|
useCustomImageUrl: boolean;
|
|
customImageUrl: string;
|
|
bypassCors: boolean;
|
|
queryStringValue: string;
|
|
queryStringKey: string;
|
|
}
|
|
|
|
export const DEFAULT_SETTINGS: SendNoteSettings = {
|
|
yamlField: "send",
|
|
titleSource: TitleSource["Note title"],
|
|
removeYaml: false,
|
|
clipboard: true,
|
|
shareUnencrypted: false,
|
|
debug: 0,
|
|
accessKey: "",
|
|
secretKey: "",
|
|
region: "eu-west-2",
|
|
bucket: "",
|
|
folder: "",
|
|
expiry: "24",
|
|
imageUrlPath: "",
|
|
useCustomEndpoint: false,
|
|
customEndpoint: "",
|
|
forcePathStyle: false,
|
|
useCustomImageUrl: false,
|
|
customImageUrl: "",
|
|
bypassCors: false,
|
|
queryStringValue: "",
|
|
queryStringKey: "",
|
|
};
|
|
|
|
export class SendNoteSettingsTab extends PluginSettingTab {
|
|
plugin: SendNotePlugin;
|
|
apikeyEl: TextComponent;
|
|
status: StatusMessage;
|
|
|
|
constructor(app: App, plugin: SendNotePlugin) {
|
|
super(app, plugin);
|
|
this.plugin = plugin;
|
|
}
|
|
|
|
display(): void {
|
|
const { containerEl } = this;
|
|
|
|
containerEl.empty();
|
|
|
|
new Setting(containerEl)
|
|
.setName("AWS access key ID")
|
|
.setDesc("AWS access key ID for a user with S3 access.")
|
|
.addText((text) => {
|
|
wrapTextWithPasswordHide(text);
|
|
text
|
|
.setPlaceholder("access key")
|
|
.setValue(this.plugin.settings.accessKey)
|
|
.onChange(async (value) => {
|
|
this.plugin.settings.accessKey = value.trim();
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
|
|
new Setting(containerEl)
|
|
.setName("AWS secret key")
|
|
.setDesc("AWS secret key for that user.")
|
|
.addText((text) => {
|
|
wrapTextWithPasswordHide(text);
|
|
text
|
|
.setPlaceholder("secret key")
|
|
.setValue(this.plugin.settings.secretKey)
|
|
.onChange(async (value) => {
|
|
this.plugin.settings.secretKey = value.trim();
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
|
|
new Setting(containerEl)
|
|
.setName("Region")
|
|
.setDesc("AWS region of the S3 bucket.")
|
|
.addText((text) =>
|
|
text
|
|
.setPlaceholder("aws region")
|
|
.setValue(this.plugin.settings.region)
|
|
.onChange(async (value) => {
|
|
this.plugin.settings.region = value.trim();
|
|
await this.plugin.saveSettings();
|
|
})
|
|
);
|
|
|
|
new Setting(containerEl)
|
|
.setName("S3 bucket")
|
|
.setDesc("S3 bucket name.")
|
|
.addText((text) =>
|
|
text
|
|
.setPlaceholder("bucket name")
|
|
.setValue(this.plugin.settings.bucket)
|
|
.onChange(async (value) => {
|
|
this.plugin.settings.bucket = value.trim();
|
|
await this.plugin.saveSettings();
|
|
})
|
|
);
|
|
|
|
new Setting(containerEl)
|
|
.setName("Bucket folder")
|
|
.setDesc("Optional folder in s3 bucket. Support the use of ${year}, ${month}, and ${day} variables.")
|
|
.addText((text) =>
|
|
text
|
|
.setPlaceholder("folder")
|
|
.setValue(this.plugin.settings.folder)
|
|
.onChange(async (value) => {
|
|
this.plugin.settings.folder = value.trim();
|
|
await this.plugin.saveSettings();
|
|
})
|
|
);
|
|
new Setting(containerEl)
|
|
.setName("Use custom endpoint")
|
|
.setDesc("Use the custom api endpoint below.")
|
|
.addToggle((toggle) => {
|
|
toggle.setValue(this.plugin.settings.useCustomEndpoint).onChange(async (value) => {
|
|
this.plugin.settings.useCustomEndpoint = value;
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
|
|
new Setting(containerEl)
|
|
.setName("Custom S3 endpoint")
|
|
.setDesc("Optionally set a custom endpoint for any S3 compatible storage provider.")
|
|
.addText((text) =>
|
|
text
|
|
.setPlaceholder("https://s3.myhost.com/")
|
|
.setValue(this.plugin.settings.customEndpoint)
|
|
.onChange(async (value) => {
|
|
value = value.match(/https?:\/\//) // Force to start http(s)://
|
|
? value
|
|
: "https://" + value;
|
|
value = value.replace(/([^\/])$/, "$1/"); // Force to end with slash
|
|
this.plugin.settings.customEndpoint = value.trim();
|
|
await this.plugin.saveSettings();
|
|
})
|
|
);
|
|
|
|
new Setting(containerEl)
|
|
.setName("S3 path style URLs")
|
|
.setDesc(
|
|
"Advanced option to force using (legacy) path-style s3 URLs (s3.myhost.com/bucket) instead of the modern AWS standard host-style (bucket.s3.myhost.com)."
|
|
)
|
|
.addToggle((toggle) => {
|
|
toggle.setValue(this.plugin.settings.forcePathStyle).onChange(async (value) => {
|
|
this.plugin.settings.forcePathStyle = value;
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
|
|
new Setting(containerEl)
|
|
.setName("Use custom image URL")
|
|
.setDesc("Use the custom image URL below.")
|
|
.addToggle((toggle) => {
|
|
toggle.setValue(this.plugin.settings.useCustomImageUrl).onChange(async (value) => {
|
|
this.plugin.settings.useCustomImageUrl = value;
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
|
|
new Setting(containerEl)
|
|
.setName("Custom image URL")
|
|
.setDesc("Advanced option to force inserting custom image URLs. This option is helpful if you are using CDN.")
|
|
.addText((text) =>
|
|
text.setValue(this.plugin.settings.customImageUrl).onChange(async (value) => {
|
|
value = value.match(/https?:\/\//) // Force to start http(s)://
|
|
? value
|
|
: "https://" + value;
|
|
value = value.replace(/([^\/])$/, "$1/"); // Force to end with slash
|
|
this.plugin.settings.customImageUrl = value.trim();
|
|
await this.plugin.saveSettings();
|
|
})
|
|
);
|
|
|
|
new Setting(containerEl)
|
|
.setName("Bypass local CORS check")
|
|
.setDesc("Bypass local CORS preflight checks - it might work on later versions of Obsidian.")
|
|
.addToggle((toggle) => {
|
|
toggle.setValue(this.plugin.settings.bypassCors).onChange(async (value) => {
|
|
this.plugin.settings.bypassCors = value;
|
|
await this.plugin.saveSettings();
|
|
});
|
|
});
|
|
new Setting(containerEl)
|
|
.setName("Query string key")
|
|
.setDesc("Appended to the end of the URL. Optional")
|
|
.addText((text) =>
|
|
text
|
|
.setPlaceholder("Empty means no query string key")
|
|
.setValue(this.plugin.settings.queryStringKey)
|
|
.onChange(async (value) => {
|
|
this.plugin.settings.queryStringKey = value;
|
|
await this.plugin.saveSettings();
|
|
})
|
|
);
|
|
|
|
new Setting(containerEl)
|
|
.setName("Query string value")
|
|
.setDesc("Appended to the end of the URL. Optional")
|
|
.addText((text) =>
|
|
text
|
|
.setPlaceholder("Empty means no query string value")
|
|
.setValue(this.plugin.settings.queryStringValue)
|
|
.onChange(async (value) => {
|
|
this.plugin.settings.queryStringValue = value;
|
|
await this.plugin.saveSettings();
|
|
})
|
|
);
|
|
|
|
// Local YAML field
|
|
new Setting(containerEl)
|
|
.setName("Frontmatter property prefix")
|
|
.setDesc(
|
|
"The frontmatter property for storing the shared link and updated time. A value of `share` will create frontmatter fields of `share_link` and `share_updated`. Use the is de-conflict frontmatter keys if needed."
|
|
)
|
|
.addText((text) =>
|
|
text
|
|
.setPlaceholder(DEFAULT_SETTINGS.yamlField)
|
|
.setValue(this.plugin.settings.yamlField)
|
|
.onChange(async (value) => {
|
|
this.plugin.settings.yamlField = value || DEFAULT_SETTINGS.yamlField;
|
|
await this.plugin.saveSettings();
|
|
})
|
|
);
|
|
|
|
new Setting(containerEl).setName("Uploads").setHeading();
|
|
|
|
// Copy to clipboard
|
|
new Setting(containerEl).setName("Copy the link to clipboard after sharing").addToggle((toggle) => {
|
|
toggle.setValue(this.plugin.settings.clipboard).onChange(async (value) => {
|
|
this.plugin.settings.clipboard = value;
|
|
await this.plugin.saveSettings();
|
|
this.display();
|
|
});
|
|
});
|
|
|
|
new Setting(containerEl).setName("Notes").setHeading();
|
|
|
|
// Strip frontmatter
|
|
new Setting(containerEl)
|
|
.setName("Remove published frontmatter/YAML")
|
|
.setDesc("Remove frontmatter/YAML/properties from the shared note")
|
|
.addToggle((toggle) => {
|
|
toggle.setValue(this.plugin.settings.removeYaml).onChange(async (value) => {
|
|
this.plugin.settings.removeYaml = value;
|
|
await this.plugin.saveSettings();
|
|
this.display();
|
|
});
|
|
});
|
|
|
|
// Share encrypted by default
|
|
new Setting(containerEl)
|
|
.setName("Share as encrypted by default")
|
|
.setDesc(
|
|
"If you turn this off, you can enable encryption for individual notes by adding a `share_encrypted` checkbox into a note and ticking it."
|
|
)
|
|
.addToggle((toggle) => {
|
|
toggle.setValue(!this.plugin.settings.shareUnencrypted).onChange(async (value) => {
|
|
this.plugin.settings.shareUnencrypted = !value;
|
|
await this.plugin.saveSettings();
|
|
this.display();
|
|
});
|
|
});
|
|
// .then((setting) => addDocs(setting, "https://docs.note.sx/notes/encryption"));
|
|
}
|
|
}
|
|
|
|
function addDocs(setting: Setting, url: string) {
|
|
setting.descEl.createEl("br");
|
|
setting.descEl.createEl("a", {
|
|
text: "View the documentation",
|
|
href: url,
|
|
});
|
|
}
|
|
|
|
const wrapTextWithPasswordHide = (text: TextComponent) => {
|
|
const hider = text.inputEl.insertAdjacentElement("beforebegin", createSpan());
|
|
if (!hider) {
|
|
return;
|
|
}
|
|
setIcon(hider as HTMLElement, "eye-off");
|
|
|
|
hider.addEventListener("click", () => {
|
|
const isText = text.inputEl.getAttribute("type") === "text";
|
|
if (isText) {
|
|
setIcon(hider as HTMLElement, "eye-off");
|
|
text.inputEl.setAttribute("type", "password");
|
|
} else {
|
|
setIcon(hider as HTMLElement, "eye");
|
|
text.inputEl.setAttribute("type", "text");
|
|
}
|
|
text.inputEl.focus();
|
|
});
|
|
text.inputEl.setAttribute("type", "password");
|
|
return text;
|
|
};
|