Various AT Protocol integrations with obsidian
1import { Notice, Plugin, WorkspaceLeaf } from "obsidian";
2import { DEFAULT_SETTINGS, AtProtoSettings, SettingTab } from "./settings";
3import { BookmarksView, VIEW_TYPE_ATMOSPHERE_BOOKMARKS } from "./views/bookmarks";
4import { publishFileAsDocument } from "./commands/publishDocument";
5import { StandardFeedView, VIEW_ATMOSPHERE_STANDARD_FEED } from "views/standardfeed";
6import { ATClient } from "lib/client";
7import { Clipper } from "lib/clipper";
8import { registerIcons } from "./icons";
9
10export default class AtmospherePlugin extends Plugin {
11 settings: AtProtoSettings = DEFAULT_SETTINGS;
12 client: ATClient;
13 clipper: Clipper;
14
15 async onload() {
16 registerIcons();
17 await this.loadSettings();
18 this.client = new ATClient();
19 this.clipper = new Clipper(this);
20
21 this.registerObsidianProtocolHandler('atmosphere-oauth', (params) => {
22 try {
23 const urlParams = new URLSearchParams();
24 for (const [key, value] of Object.entries(params)) {
25 if (value) {
26 urlParams.set(key, String(value));
27 }
28 }
29 this.client.handleOAuthCallback(urlParams);
30 new Notice('Authentication completed! Processing...');
31 } catch (error) {
32 console.error('Error handling OAuth callback:', error);
33 new Notice('Authentication error. Please try again.');
34 }
35 });
36
37 this.registerView(VIEW_TYPE_ATMOSPHERE_BOOKMARKS, (leaf) => {
38 return new BookmarksView(leaf, this);
39 });
40
41 this.registerView(VIEW_ATMOSPHERE_STANDARD_FEED, (leaf) => {
42 return new StandardFeedView(leaf, this);
43 });
44
45 this.addRibbonIcon("layers", "Atmosphere bookmarks", () => {
46 void this.activateView(VIEW_TYPE_ATMOSPHERE_BOOKMARKS);
47 });
48
49 this.addRibbonIcon("rss", "Atmosphere feed", () => {
50 void this.activateView(VIEW_ATMOSPHERE_STANDARD_FEED);
51 });
52
53 this.addCommand({
54 id: "open-bookmarks",
55 name: "Open bookmarks",
56 callback: () => { void this.activateView(VIEW_TYPE_ATMOSPHERE_BOOKMARKS); },
57 });
58
59 this.addCommand({
60 id: "open-feed",
61 name: "Open feed",
62 callback: () => { void this.activateView(VIEW_ATMOSPHERE_STANDARD_FEED); },
63 });
64
65 this.addCommand({
66 id: "publish-note",
67 name: "Publish note",
68 editorCheckCallback: (checking: boolean,) => {
69 const file = this.app.workspace.getActiveFile();
70
71 if (file) {
72 if (!checking) {
73 void publishFileAsDocument(this)
74 }
75
76 return true
77 }
78
79 return false;
80 },
81 });
82
83 this.addSettingTab(new SettingTab(this.app, this));
84 }
85
86 async checkAuth() {
87 if (this.client.loggedIn) {
88 return true;
89 }
90 if (this.settings.did) {
91 try {
92 await this.client.restoreSession(this.settings.did);
93 return true
94 } catch (e) {
95 console.error("Failed to restore session:", e);
96 this.settings.did = undefined;
97 await this.saveSettings();
98 new Notice("Session expired. Please login by opening settings");
99 return false;
100 }
101 }
102 new Notice("Please log in by opening settings");
103 return false;
104 }
105
106 async activateView(v: string) {
107 if (!await this.checkAuth()) {
108 return;
109 }
110
111 const { workspace } = this.app;
112
113 let leaf: WorkspaceLeaf | null = null;
114 const leaves = workspace.getLeavesOfType(v);
115
116 if (leaves.length > 0) {
117 // A leaf with our view already exists, use that
118 leaf = leaves[0] as WorkspaceLeaf;
119 void workspace.revealLeaf(leaf);
120 return;
121 }
122
123 // Our view could not be found in the workspace, create a new leaf
124 leaf = workspace.getMostRecentLeaf()
125 await leaf?.setViewState({ type: v, active: true });
126
127 // "Reveal" the leaf in case it is in a collapsed sidebar
128 if (leaf) {
129 void workspace.revealLeaf(leaf);
130 }
131 }
132
133 async loadSettings() {
134 const saved = await this.loadData() as Partial<AtProtoSettings>;
135 this.settings = Object.assign({}, DEFAULT_SETTINGS, saved, {
136 bookmarks: {
137 ...DEFAULT_SETTINGS.bookmarks,
138 ...saved?.bookmarks,
139 enabledSources: {
140 ...DEFAULT_SETTINGS.bookmarks.enabledSources,
141 ...saved?.bookmarks?.enabledSources,
142 },
143 },
144 publish: {
145 ...DEFAULT_SETTINGS.publish,
146 ...saved?.publish,
147 },
148 });
149 }
150
151 async saveSettings() {
152 await this.saveData(this.settings);
153 }
154
155 onunload() { }
156}