From 35b8762fb3d601f3ad2e26220130650f7aa95b19 Mon Sep 17 00:00:00 2001 From: creamhead Date: Sat, 13 Jun 2026 17:46:59 +0900 Subject: [PATCH] v1.3.3: Show authorized status in settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Display "✅ Authorized" in the Auth setting row when an auth token is already saved, so users can confirm authentication at a glance. Co-Authored-By: Claude Sonnet 4.6 --- main.js | 8 ++++---- manifest.json | 2 +- package.json | 2 +- src/main.ts | 6 +++++- versions.json | 3 ++- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/main.js b/main.js index aedae47..29207a9 100644 --- a/main.js +++ b/main.js @@ -3,12 +3,12 @@ THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source, please visit the github repository of this plugin */ -var Z=Object.defineProperty;var it=Object.getOwnPropertyDescriptor;var rt=Object.getOwnPropertyNames;var at=Object.prototype.hasOwnProperty;var ot=(S,p)=>{for(var t in p)Z(S,t,{get:p[t],enumerable:!0})},lt=(S,p,t,o)=>{if(p&&typeof p=="object"||typeof p=="function")for(let r of rt(p))!at.call(S,r)&&r!==t&&Z(S,r,{get:()=>p[r],enumerable:!(o=it(p,r))||o.enumerable});return S};var ct=S=>lt(Z({},"__esModule",{value:!0}),S);var ut={};ot(ut,{default:()=>X});module.exports=ct(ut);var f=require("obsidian");function I(S){function p(w,k){return w<>>32-k}function t(w,k){let x=w&1073741824,A=k&1073741824,E=w&2147483648,C=k&2147483648,b=(w&1073741823)+(k&1073741823);return x&A?b^2147483648^E^C:x|A?b&1073741824?b^3221225472^E^C:b^1073741824^E^C:b^E^C}function o(w,k,x){return w&k|~w&x}function r(w,k,x){return w&x|k&~x}function c(w,k,x){return w^k^x}function d(w,k,x){return k^(w|~x)}function m(w,k,x,A,E,C,b){return w=t(w,t(t(o(k,x,A),E),b)),t(p(w,C),k)}function g(w,k,x,A,E,C,b){return w=t(w,t(t(r(k,x,A),E),b)),t(p(w,C),k)}function y(w,k,x,A,E,C,b){return w=t(w,t(t(c(k,x,A),E),b)),t(p(w,C),k)}function u(w,k,x,A,E,C,b){return w=t(w,t(t(d(k,x,A),E),b)),t(p(w,C),k)}function h(w){let k,x=w.length,A=x+8,C=((A-A%64)/64+1)*16,b=Array(C-1),J=0,N=0;for(;N>>29,b}function F(w){let k="",x="",A,E;for(E=0;E<=3;E++)A=w>>>E*8&255,x="0"+A.toString(16),k=k+x.substr(x.length-2,2);return k}function T(w){w=w.replace(/\r\n/g,` -`);let k="";for(let x=0;x127&&A<2048?(k+=String.fromCharCode(A>>6|192),k+=String.fromCharCode(A&63|128)):(k+=String.fromCharCode(A>>12|224),k+=String.fromCharCode(A>>6&63|128),k+=String.fromCharCode(A&63|128))}return k}let a=Array(),l,P,D,$,v,n,i,s,e,B=7,R=12,L=17,_=22,M=5,O=9,j=14,W=20,V=4,K=11,U=16,H=23,G=6,q=10,Q=15,Y=21;for(S=T(S),a=h(S),n=1732584193,i=4023233417,s=2562383102,e=271733878,l=0;l{this.checkAuth()&&await this.fetchAndProcessTasks("status:incomplete",!1,r=>{this.insertTasksToEditor(t,r)})}}),this.addCommand({id:"select-rtm-tasks",name:"Select and import tasks",editorCallback:async(t,o)=>{this.checkAuth()&&await this.fetchAndProcessTasks("status:incomplete",!0,r=>{this.insertTasksToEditor(t,r)})}}),this.addCommand({id:"import-rtm-tasks-custom",name:"Download tasks (Custom Filter)",editorCallback:async(t,o)=>{this.checkAuth()&&new tt(this.app,async r=>{await this.fetchAndProcessTasks(r,!0,c=>{this.insertTasksToEditor(t,c)})}).open()}}),this.addCommand({id:"create-note-from-rtm-task",name:"Create note from RTM task",callback:async()=>{this.checkAuth()&&await this.fetchAndProcessTasks("status:incomplete",!0,t=>{this.createNotesFromTasks(t)})}}),this.addCommand({id:"add-rtm-task",name:"Add cursor line to RTM",editorCallback:async(t,o)=>{this.checkAuth()&&await this.addTaskFromEditor(t,o)}}),this.addCommand({id:"complete-rtm-task",name:"Complete task at cursor",editorCallback:async(t,o)=>{this.checkAuth()&&await this.completeTaskInEditor(t)}})}checkAuth(){return!this.settings.apiKey||!this.settings.sharedSecret?(new f.Notice("Please set your API Key and Secret in settings."),!1):this.settings.authToken?!0:(new f.Notice("Please authenticate with RTM from settings."),!1)}async fetchListsMap(){let t={};try{let o=await this.callRtmApi("rtm.lists.getList");if(o.rsp.lists&&o.rsp.lists.list){let r=Array.isArray(o.rsp.lists.list)?o.rsp.lists.list:[o.rsp.lists.list];for(let c of r)c&&c.id&&c.name&&(t[c.id]=c.name)}}catch(o){console.error("List fetch error:",o)}return t}async fetchAndProcessTasks(t,o,r){new f.Notice("Fetching tasks...");try{let c=await this.fetchListsMap(),d=await this.callRtmApi("rtm.tasks.getList",{filter:t,notes:"1"});if(!d.rsp.tasks||!d.rsp.tasks.list){new f.Notice("No tasks found.");return}let m=[],g=Array.isArray(d.rsp.tasks.list)?d.rsp.tasks.list:[d.rsp.tasks.list];for(let y of g){if(!y.taskseries)continue;let u=Array.isArray(y.taskseries)?y.taskseries:[y.taskseries];for(let h of u){let F=h.name,T=Array.isArray(h.task)?h.task[0]:h.task,a=y.id;!a&&h.list_id&&(a=h.list_id),a||(a="MISSING");let l="";T.due&&T.due!==""&&(l=`\u{1F4C5} ${T.due.split("T")[0]}`);let P="";T.start&&T.start!==""&&(P=`\u{1F6EB} ${T.start.split("T")[0]}`);let D="";switch(T.priority){case"1":D="\u{1F53A}";break;case"2":D="\u{1F53C}";break;case"3":D="\u{1F53D}";break}let $="",v=c[a];v&&($=v);let n=[];h.tags&&h.tags.tag&&(Array.isArray(h.tags.tag)?h.tags.tag:[h.tags.tag]).forEach(e=>n.push(e));let i=[];h.notes&&h.notes.note&&(Array.isArray(h.notes.note)?h.notes.note:[h.notes.note]).forEach(e=>{let B=e.$t||e.toString();B&&i.push(B)}),m.push({name:F,due:l,start:P,priority:D,listName:$,tags:n,notes:i,rtmId:{list:a,series:h.id,task:T.id},rawPriority:T.priority,rawDue:T.due})}}o?new z(this.app,m,y=>{r(y)}).open():r(m)}catch(c){console.error(c),new f.Notice("Fetch error.")}}insertTasksToEditor(t,o){if(o.length===0){new f.Notice("No tasks selected.");return}let r="";for(let c of o){let d="";c.listName&&(d=` #${c.listName.replace(/[\s,.]+/g,"_")}`);let m=c.tags.map(h=>` #${h}`).join(""),g=`[\u{1F42E}](rtm:${c.rtmId.list}:${c.rtmId.series}:${c.rtmId.task})`,y=c.priority?` ${c.priority}`:"",u=c.due?` ${c.due}`:"";if(r+=`- [ ] ${g} ${c.name}${y}${u}${d}${m} +var X=Object.defineProperty;var it=Object.getOwnPropertyDescriptor;var rt=Object.getOwnPropertyNames;var at=Object.prototype.hasOwnProperty;var ot=(E,p)=>{for(var t in p)X(E,t,{get:p[t],enumerable:!0})},lt=(E,p,t,d)=>{if(p&&typeof p=="object"||typeof p=="function")for(let r of rt(p))!at.call(E,r)&&r!==t&&X(E,r,{get:()=>p[r],enumerable:!(d=it(p,r))||d.enumerable});return E};var ct=E=>lt(X({},"__esModule",{value:!0}),E);var ut={};ot(ut,{default:()=>J});module.exports=ct(ut);var f=require("obsidian");function I(E){function p(w,k){return w<>>32-k}function t(w,k){let x=w&1073741824,A=k&1073741824,S=w&2147483648,C=k&2147483648,b=(w&1073741823)+(k&1073741823);return x&A?b^2147483648^S^C:x|A?b&1073741824?b^3221225472^S^C:b^1073741824^S^C:b^S^C}function d(w,k,x){return w&k|~w&x}function r(w,k,x){return w&x|k&~x}function c(w,k,x){return w^k^x}function l(w,k,x){return k^(w|~x)}function m(w,k,x,A,S,C,b){return w=t(w,t(t(d(k,x,A),S),b)),t(p(w,C),k)}function g(w,k,x,A,S,C,b){return w=t(w,t(t(r(k,x,A),S),b)),t(p(w,C),k)}function y(w,k,x,A,S,C,b){return w=t(w,t(t(c(k,x,A),S),b)),t(p(w,C),k)}function u(w,k,x,A,S,C,b){return w=t(w,t(t(l(k,x,A),S),b)),t(p(w,C),k)}function h(w){let k,x=w.length,A=x+8,C=((A-A%64)/64+1)*16,b=Array(C-1),z=0,N=0;for(;N>>29,b}function F(w){let k="",x="",A,S;for(S=0;S<=3;S++)A=w>>>S*8&255,x="0"+A.toString(16),k=k+x.substr(x.length-2,2);return k}function T(w){w=w.replace(/\r\n/g,` +`);let k="";for(let x=0;x127&&A<2048?(k+=String.fromCharCode(A>>6|192),k+=String.fromCharCode(A&63|128)):(k+=String.fromCharCode(A>>12|224),k+=String.fromCharCode(A>>6&63|128),k+=String.fromCharCode(A&63|128))}return k}let a=Array(),o,P,D,$,v,n,i,s,e,B=7,R=12,L=17,_=22,M=5,O=9,j=14,W=20,V=4,K=11,U=16,H=23,G=6,q=10,Q=15,Y=21;for(E=T(E),a=h(E),n=1732584193,i=4023233417,s=2562383102,e=271733878,o=0;o{this.checkAuth()&&await this.fetchAndProcessTasks("status:incomplete",!1,r=>{this.insertTasksToEditor(t,r)})}}),this.addCommand({id:"select-rtm-tasks",name:"Select and import tasks",editorCallback:async(t,d)=>{this.checkAuth()&&await this.fetchAndProcessTasks("status:incomplete",!0,r=>{this.insertTasksToEditor(t,r)})}}),this.addCommand({id:"import-rtm-tasks-custom",name:"Download tasks (Custom Filter)",editorCallback:async(t,d)=>{this.checkAuth()&&new tt(this.app,async r=>{await this.fetchAndProcessTasks(r,!0,c=>{this.insertTasksToEditor(t,c)})}).open()}}),this.addCommand({id:"create-note-from-rtm-task",name:"Create note from RTM task",callback:async()=>{this.checkAuth()&&await this.fetchAndProcessTasks("status:incomplete",!0,t=>{this.createNotesFromTasks(t)})}}),this.addCommand({id:"add-rtm-task",name:"Add cursor line to RTM",editorCallback:async(t,d)=>{this.checkAuth()&&await this.addTaskFromEditor(t,d)}}),this.addCommand({id:"complete-rtm-task",name:"Complete task at cursor",editorCallback:async(t,d)=>{this.checkAuth()&&await this.completeTaskInEditor(t)}})}checkAuth(){return!this.settings.apiKey||!this.settings.sharedSecret?(new f.Notice("Please set your API Key and Secret in settings."),!1):this.settings.authToken?!0:(new f.Notice("Please authenticate with RTM from settings."),!1)}async fetchListsMap(){let t={};try{let d=await this.callRtmApi("rtm.lists.getList");if(d.rsp.lists&&d.rsp.lists.list){let r=Array.isArray(d.rsp.lists.list)?d.rsp.lists.list:[d.rsp.lists.list];for(let c of r)c&&c.id&&c.name&&(t[c.id]=c.name)}}catch(d){console.error("List fetch error:",d)}return t}async fetchAndProcessTasks(t,d,r){new f.Notice("Fetching tasks...");try{let c=await this.fetchListsMap(),l=await this.callRtmApi("rtm.tasks.getList",{filter:t,notes:"1"});if(!l.rsp.tasks||!l.rsp.tasks.list){new f.Notice("No tasks found.");return}let m=[],g=Array.isArray(l.rsp.tasks.list)?l.rsp.tasks.list:[l.rsp.tasks.list];for(let y of g){if(!y.taskseries)continue;let u=Array.isArray(y.taskseries)?y.taskseries:[y.taskseries];for(let h of u){let F=h.name,T=Array.isArray(h.task)?h.task[0]:h.task,a=y.id;!a&&h.list_id&&(a=h.list_id),a||(a="MISSING");let o="";T.due&&T.due!==""&&(o=`\u{1F4C5} ${T.due.split("T")[0]}`);let P="";T.start&&T.start!==""&&(P=`\u{1F6EB} ${T.start.split("T")[0]}`);let D="";switch(T.priority){case"1":D="\u{1F53A}";break;case"2":D="\u{1F53C}";break;case"3":D="\u{1F53D}";break}let $="",v=c[a];v&&($=v);let n=[];h.tags&&h.tags.tag&&(Array.isArray(h.tags.tag)?h.tags.tag:[h.tags.tag]).forEach(e=>n.push(e));let i=[];h.notes&&h.notes.note&&(Array.isArray(h.notes.note)?h.notes.note:[h.notes.note]).forEach(e=>{let B=e.$t||e.toString();B&&i.push(B)}),m.push({name:F,due:o,start:P,priority:D,listName:$,tags:n,notes:i,rtmId:{list:a,series:h.id,task:T.id},rawPriority:T.priority,rawDue:T.due})}}d?new Z(this.app,m,y=>{r(y)}).open():r(m)}catch(c){console.error(c),new f.Notice("Fetch error.")}}insertTasksToEditor(t,d){if(d.length===0){new f.Notice("No tasks selected.");return}let r="";for(let c of d){let l="";c.listName&&(l=` #${c.listName.replace(/[\s,.]+/g,"_")}`);let m=c.tags.map(h=>` #${h}`).join(""),g=`[\u{1F42E}](rtm:${c.rtmId.list}:${c.rtmId.series}:${c.rtmId.task})`,y=c.priority?` ${c.priority}`:"",u=c.due?` ${c.due}`:"";if(r+=`- [ ] ${g} ${c.name}${y}${u}${l}${m} `,this.settings.importWithNotesAndLink){let h=`https://www.rememberthemilk.com/app/#all/${c.rtmId.task}`;r+=` - RTM Link: ${h} `,c.notes&&c.notes.length>0&&c.notes.forEach(F=>{F.split(` `).forEach(a=>{a.trim()!==""&&(r+=` - Note: ${a} -`)})})}}t.replaceSelection(r),new f.Notice(`${o.length} tasks inserted.`)}async createNotesFromTasks(t){if(t.length===0){new f.Notice("No tasks selected.");return}let o=0;for(let r of t){let c=r.name.replace(/[\\/:*?"<>|]/g,"-"),d=this.settings.noteCreationFolder.trim();d.endsWith("/")&&(d=d.slice(0,-1)),d.startsWith("/")&&(d=d.slice(1)),d&&await this.ensureFolderExists(d);let m=d?`${d}/${c}.md`:`${c}.md`;this.app.vault.getAbstractFileByPath(m)&&(m=d?`${d}/${c}-${Date.now()}.md`:`${c}-${Date.now()}.md`);let g=r.tags.map(h=>`#${h}`).join(" "),y=r.listName?`#${r.listName.replace(/[\s,.]+/g,"_")}`:"",u=`# ${r.name} +`)})})}}t.replaceSelection(r),new f.Notice(`${d.length} tasks inserted.`)}async createNotesFromTasks(t){if(t.length===0){new f.Notice("No tasks selected.");return}let d=0;for(let r of t){let c=r.name.replace(/[\\/:*?"<>|]/g,"-"),l=this.settings.noteCreationFolder.trim();l.endsWith("/")&&(l=l.slice(0,-1)),l.startsWith("/")&&(l=l.slice(1)),l&&await this.ensureFolderExists(l);let m=l?`${l}/${c}.md`:`${c}.md`;this.app.vault.getAbstractFileByPath(m)&&(m=l?`${l}/${c}-${Date.now()}.md`:`${c}-${Date.now()}.md`);let g=r.tags.map(h=>`#${h}`).join(" "),y=r.listName?`#${r.listName.replace(/[\s,.]+/g,"_")}`:"",u=`# ${r.name} `;u+=`- **RTM Link**: [\u{1F42E} Open Task](rtm:${r.rtmId.list}:${r.rtmId.series}:${r.rtmId.task}) `,r.due&&(u+=`- **Due**: ${r.due} @@ -23,4 +23,4 @@ var Z=Object.defineProperty;var it=Object.getOwnPropertyDescriptor;var rt=Object `,r.notes.forEach(h=>{u+=`${h} `})):u+=`(No notes in RTM) -`;try{await this.app.vault.create(m,u),o++}catch(h){console.error(`Failed to create file: ${m}`,h),new f.Notice(`Failed to create note for "${r.name}"`)}}new f.Notice(`${o} notes created.`)}async addTaskFromEditor(t,o){let r=t.getCursor(),d=t.getLine(r.line).replace(/^[-*] \[[ x]\] /,"").replace(/^[-*] /,"").trim();if(!d){new f.Notice("Task name is empty.");return}new f.Notice(`Adding: ${d}`);let m=o.file?o.file.basename:"";try{let g=await this.getTimeline(),y=await this.callRtmApi("rtm.tasks.add",{timeline:g,name:d,parse:"1"}),u=Array.isArray(y.rsp.list)?y.rsp.list[0]:y.rsp.list,h=u.id,F=Array.isArray(u.taskseries)?u.taskseries[0]:u.taskseries,T=F.id,l=(Array.isArray(F.task)?F.task[0]:F.task).id;if(this.settings.defaultDueForNewTask==="today")try{await this.callRtmApi("rtm.tasks.setDueDate",{timeline:g,list_id:h,taskseries_id:T,task_id:l,due:"today",parse:"1"})}catch($){console.error("Failed to set due date",$)}if(m)try{await this.callRtmApi("rtm.tasks.notes.add",{timeline:g,list_id:h,taskseries_id:T,task_id:l,note_title:"Obsidian Link",note_text:`From Obsidian Note: [[${m}]]`})}catch($){console.error("Failed to add note to RTM task",$)}let D=`- [ ] ${`[\u{1F42E}](rtm:${h}:${T}:${l})`} ${d}`;t.setLine(r.line,D),new f.Notice("Added to RTM!")}catch(g){console.error("Add Task Error:",g),new f.Notice("Add error.")}}async completeTaskInEditor(t){let o=t.getCursor(),r=t.getLine(o.line),c=/\(rtm:([\w\d]+):([\w\d]+):([\w\d]+)\)/,d=r.match(c);if(!d){new f.Notice("Error: No RTM Link found.");return}let m=d[1],g=d[2],y=d[3];if(!m||m==="MISSING"){new f.Notice("Error: List ID invalid.");return}new f.Notice("Completing task...");try{let u=await this.getTimeline();await this.callRtmApi("rtm.tasks.complete",{timeline:u,list_id:m,taskseries_id:g,task_id:y});let h=r.replace("- [ ]","- [x]");t.setLine(o.line,h),new f.Notice("Task completed!")}catch(u){console.error("Completion Error:",u),new f.Notice("Completion error.")}}async getTimeline(){if(this.timeline)return this.timeline;let t=await this.callRtmApi("rtm.timelines.create");return this.timeline=t.rsp.timeline,this.timeline}async callRtmApi(t,o={}){var h;let r=this.settings.apiKey,c=this.settings.sharedSecret;if(!r||!c)throw new Error("API Key/Secret missing");let d={...o,method:t,api_key:r,format:"json",auth_token:this.settings.authToken},m=Object.keys(d).sort(),g=c;for(let F of m)g+=F+d[F];d.api_sig=I(g);let y=`${nt}?${new URLSearchParams(d).toString()}`,u=await(0,f.requestUrl)({url:y});if(u.json.rsp.stat!=="ok")throw console.error("API Error:",u.json),new Error(`API Error: ${(h=u.json.rsp.err)==null?void 0:h.msg}`);return u.json}async ensureFolderExists(t){if(!t)return;let o=t.split("/"),r="";for(let c of o)if(c&&(r=r===""?c:`${r}/${c}`,!this.app.vault.getAbstractFileByPath(r)))try{await this.app.vault.createFolder(r)}catch(d){console.error(`Failed to create folder: ${r}`,d)}}async loadSettings(){this.settings=Object.assign({},mt,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}},z=class extends f.Modal{constructor(p,t,o){super(p),this.tasks=t,this.selected=new Array(t.length).fill(!1),this.onSubmit=o}onOpen(){let{contentEl:p}=this;p.createEl("h2",{text:`Select Tasks (${this.tasks.length})`});let t=p.createDiv();t.style.maxHeight="400px",t.style.overflowY="auto",t.style.marginBottom="10px",this.tasks.forEach((d,m)=>{var F;let g=t.createDiv();g.style.display="flex",g.style.alignItems="center",g.style.padding="5px 0",g.style.borderBottom="1px solid var(--background-modifier-border)";let y=g.createEl("input",{type:"checkbox"});y.checked=(F=this.selected[m])!=null?F:!1,y.onchange=T=>{this.selected[m]=T.target.checked};let u=g.createSpan();u.style.marginLeft="10px";let h=`${d.name}`;d.listName&&(h+=` [${d.listName}]`),d.due&&(h+=` ${d.due}`),d.notes&&d.notes.length>0&&(h+=" \u{1F4DD}"),u.innerHTML=h});let o=p.createDiv();o.style.display="flex",o.style.justifyContent="flex-end",o.style.gap="10px";let r=o.createEl("button",{text:"Select All"});r.onclick=()=>{let d=this.selected.every(Boolean);this.selected.fill(!d),t.querySelectorAll('input[type="checkbox"]').forEach(g=>g.checked=!d),r.textContent=d?"Select All":"Deselect All"};let c=o.createEl("button",{text:"Process Selected"});c.className="mod-cta",c.onclick=()=>{let d=this.tasks.filter((m,g)=>this.selected[g]);this.onSubmit(d),this.close()}}onClose(){this.contentEl.empty()}},tt=class extends f.Modal{constructor(p,t){super(p),this.onSubmit=t}onOpen(){let{contentEl:p}=this;p.createEl("h3",{text:"Filter RTM Tasks"}),new f.Setting(p).setName("Search Query").setDesc("e.g. list:Inbox, due:today").addText(t=>t.setValue("status:incomplete").onChange(o=>{}).inputEl.addEventListener("keydown",o=>{o.key==="Enter"&&(this.onSubmit(t.getValue()),this.close())})),new f.Setting(p).addButton(t=>t.setButtonText("Search").setCta().onClick(()=>{let o=p.querySelector("input");o&&(this.onSubmit(o.value),this.close())}))}onClose(){this.contentEl.empty()}},et=class extends f.PluginSettingTab{constructor(p,t){super(p,t),this.plugin=t}display(){let{containerEl:p}=this;p.empty(),p.createEl("h2",{text:"Remember The Milk Settings"});let t="6/4/2026, 9:42:32 AM";p.createEl("p",{text:`Version: ${this.plugin.manifest.version} (Build: ${t})`,cls:"setting-item-description"}).style.marginBottom="2em",new f.Setting(p).setName("API Key").addText(o=>o.setValue(this.plugin.settings.apiKey).onChange(async r=>{this.plugin.settings.apiKey=r,await this.plugin.saveSettings()})),new f.Setting(p).setName("Shared Secret").addText(o=>o.setValue(this.plugin.settings.sharedSecret).onChange(async r=>{this.plugin.settings.sharedSecret=r,await this.plugin.saveSettings()})),new f.Setting(p).setName("Auth").addButton(o=>o.setButtonText("Start Auth").onClick(async()=>await this.startAuthProcess())),new f.Setting(p).setName("Default Due Date").setDesc("Setting for new tasks created from editor").addDropdown(o=>o.addOption("none","No Due Date").addOption("today","Today").setValue(this.plugin.settings.defaultDueForNewTask).onChange(async r=>{this.plugin.settings.defaultDueForNewTask=r,await this.plugin.saveSettings()})),new f.Setting(p).setName("Note Creation Folder").setDesc("Folder to create notes from RTM tasks.").addDropdown(o=>{o.addOption("","/ (Vault Root)"),this.app.vault.getAllLoadedFiles().filter(c=>c instanceof f.TFolder).forEach(c=>{c.path!=="/"&&o.addOption(c.path,c.path)}),o.setValue(this.plugin.settings.noteCreationFolder),o.onChange(async c=>{this.plugin.settings.noteCreationFolder=c,await this.plugin.saveSettings()})}),new f.Setting(p).setName("Import with Notes and RTM Link").setDesc("When inserting tasks to the editor, also include RTM task notes and links as sub-items.").addToggle(o=>o.setValue(this.plugin.settings.importWithNotesAndLink).onChange(async r=>{this.plugin.settings.importWithNotesAndLink=r,await this.plugin.saveSettings()}))}async startAuthProcess(){let p=this.plugin,t=p.settings.apiKey,o=p.settings.sharedSecret;if(!t||!o){new f.Notice("Enter keys first");return}let r={method:"rtm.auth.getFrob",api_key:t,format:"json"},c=o;Object.keys(r).sort().forEach(h=>c+=h+r[h]),r.api_sig=I(c);let m=(await(0,f.requestUrl)({url:`${nt}?${new URLSearchParams(r).toString()}`})).json.rsp.frob,g={api_key:t,perms:"delete",frob:m},y=o;Object.keys(g).sort().forEach(h=>y+=h+g[h]);let u=I(y);window.open(`${dt}?api_key=${t}&perms=delete&frob=${m}&api_sig=${u}`),new st(this.app,m,p,async()=>{this.display()}).open()}},st=class extends f.Modal{constructor(p,t,o,r){super(p),this.frob=t,this.plugin=o,this.onSuccess=r}onOpen(){let{contentEl:p}=this;new f.Setting(p).addButton(t=>t.setButtonText("Finish Auth").setCta().onClick(async()=>{let o=this.plugin.settings.apiKey,r=this.plugin.settings.sharedSecret,c={method:"rtm.auth.getToken",api_key:o,format:"json",frob:this.frob},d=r;Object.keys(c).sort().forEach(m=>d+=m+c[m]),c.api_sig=I(d);try{let m=await(0,f.requestUrl)({url:`${nt}?${new URLSearchParams(c).toString()}`});m.json.rsp.auth?(this.plugin.settings.authToken=m.json.rsp.auth.token,await this.plugin.saveSettings(),new f.Notice("Success!"),this.onSuccess(),this.close()):new f.Notice("Failed.")}catch(m){new f.Notice("Error.")}}))}onClose(){this.contentEl.empty()}}; +`;try{await this.app.vault.create(m,u),d++}catch(h){console.error(`Failed to create file: ${m}`,h),new f.Notice(`Failed to create note for "${r.name}"`)}}new f.Notice(`${d} notes created.`)}async addTaskFromEditor(t,d){let r=t.getCursor(),l=t.getLine(r.line).replace(/^[-*] \[[ x]\] /,"").replace(/^[-*] /,"").trim();if(!l){new f.Notice("Task name is empty.");return}new f.Notice(`Adding: ${l}`);let m=d.file?d.file.basename:"";try{let g=await this.getTimeline(),y=await this.callRtmApi("rtm.tasks.add",{timeline:g,name:l,parse:"1"}),u=Array.isArray(y.rsp.list)?y.rsp.list[0]:y.rsp.list,h=u.id,F=Array.isArray(u.taskseries)?u.taskseries[0]:u.taskseries,T=F.id,o=(Array.isArray(F.task)?F.task[0]:F.task).id;if(this.settings.defaultDueForNewTask==="today")try{await this.callRtmApi("rtm.tasks.setDueDate",{timeline:g,list_id:h,taskseries_id:T,task_id:o,due:"today",parse:"1"})}catch($){console.error("Failed to set due date",$)}if(m)try{await this.callRtmApi("rtm.tasks.notes.add",{timeline:g,list_id:h,taskseries_id:T,task_id:o,note_title:"Obsidian Link",note_text:`From Obsidian Note: [[${m}]]`})}catch($){console.error("Failed to add note to RTM task",$)}let D=`- [ ] ${`[\u{1F42E}](rtm:${h}:${T}:${o})`} ${l}`;t.setLine(r.line,D),new f.Notice("Added to RTM!")}catch(g){console.error("Add Task Error:",g),new f.Notice("Add error.")}}async completeTaskInEditor(t){let d=t.getCursor(),r=t.getLine(d.line),c=/\(rtm:([\w\d]+):([\w\d]+):([\w\d]+)\)/,l=r.match(c);if(!l){new f.Notice("Error: No RTM Link found.");return}let m=l[1],g=l[2],y=l[3];if(!m||m==="MISSING"){new f.Notice("Error: List ID invalid.");return}new f.Notice("Completing task...");try{let u=await this.getTimeline();await this.callRtmApi("rtm.tasks.complete",{timeline:u,list_id:m,taskseries_id:g,task_id:y});let h=r.replace("- [ ]","- [x]");t.setLine(d.line,h),new f.Notice("Task completed!")}catch(u){console.error("Completion Error:",u),new f.Notice("Completion error.")}}async getTimeline(){if(this.timeline)return this.timeline;let t=await this.callRtmApi("rtm.timelines.create");return this.timeline=t.rsp.timeline,this.timeline}async callRtmApi(t,d={}){var h;let r=this.settings.apiKey,c=this.settings.sharedSecret;if(!r||!c)throw new Error("API Key/Secret missing");let l={...d,method:t,api_key:r,format:"json",auth_token:this.settings.authToken},m=Object.keys(l).sort(),g=c;for(let F of m)g+=F+l[F];l.api_sig=I(g);let y=`${nt}?${new URLSearchParams(l).toString()}`,u=await(0,f.requestUrl)({url:y});if(u.json.rsp.stat!=="ok")throw console.error("API Error:",u.json),new Error(`API Error: ${(h=u.json.rsp.err)==null?void 0:h.msg}`);return u.json}async ensureFolderExists(t){if(!t)return;let d=t.split("/"),r="";for(let c of d)if(c&&(r=r===""?c:`${r}/${c}`,!this.app.vault.getAbstractFileByPath(r)))try{await this.app.vault.createFolder(r)}catch(l){console.error(`Failed to create folder: ${r}`,l)}}async loadSettings(){this.settings=Object.assign({},mt,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}},Z=class extends f.Modal{constructor(p,t,d){super(p),this.tasks=t,this.selected=new Array(t.length).fill(!1),this.onSubmit=d}onOpen(){let{contentEl:p}=this;p.createEl("h2",{text:`Select Tasks (${this.tasks.length})`});let t=p.createDiv();t.style.maxHeight="400px",t.style.overflowY="auto",t.style.marginBottom="10px",this.tasks.forEach((l,m)=>{var F;let g=t.createDiv();g.style.display="flex",g.style.alignItems="center",g.style.padding="5px 0",g.style.borderBottom="1px solid var(--background-modifier-border)";let y=g.createEl("input",{type:"checkbox"});y.checked=(F=this.selected[m])!=null?F:!1,y.onchange=T=>{this.selected[m]=T.target.checked};let u=g.createSpan();u.style.marginLeft="10px";let h=`${l.name}`;l.listName&&(h+=` [${l.listName}]`),l.due&&(h+=` ${l.due}`),l.notes&&l.notes.length>0&&(h+=" \u{1F4DD}"),u.innerHTML=h});let d=p.createDiv();d.style.display="flex",d.style.justifyContent="flex-end",d.style.gap="10px";let r=d.createEl("button",{text:"Select All"});r.onclick=()=>{let l=this.selected.every(Boolean);this.selected.fill(!l),t.querySelectorAll('input[type="checkbox"]').forEach(g=>g.checked=!l),r.textContent=l?"Select All":"Deselect All"};let c=d.createEl("button",{text:"Process Selected"});c.className="mod-cta",c.onclick=()=>{let l=this.tasks.filter((m,g)=>this.selected[g]);this.onSubmit(l),this.close()}}onClose(){this.contentEl.empty()}},tt=class extends f.Modal{constructor(p,t){super(p),this.onSubmit=t}onOpen(){let{contentEl:p}=this;p.createEl("h3",{text:"Filter RTM Tasks"}),new f.Setting(p).setName("Search Query").setDesc("e.g. list:Inbox, due:today").addText(t=>t.setValue("status:incomplete").onChange(d=>{}).inputEl.addEventListener("keydown",d=>{d.key==="Enter"&&(this.onSubmit(t.getValue()),this.close())})),new f.Setting(p).addButton(t=>t.setButtonText("Search").setCta().onClick(()=>{let d=p.querySelector("input");d&&(this.onSubmit(d.value),this.close())}))}onClose(){this.contentEl.empty()}},et=class extends f.PluginSettingTab{constructor(p,t){super(p,t),this.plugin=t}display(){let{containerEl:p}=this;p.empty(),p.createEl("h2",{text:"Remember The Milk Settings"});let t="6/13/2026, 5:46:47 PM";p.createEl("p",{text:`Version: ${this.plugin.manifest.version} (Build: ${t})`,cls:"setting-item-description"}).style.marginBottom="2em",new f.Setting(p).setName("API Key").addText(r=>r.setValue(this.plugin.settings.apiKey).onChange(async c=>{this.plugin.settings.apiKey=c,await this.plugin.saveSettings()})),new f.Setting(p).setName("Shared Secret").addText(r=>r.setValue(this.plugin.settings.sharedSecret).onChange(async c=>{this.plugin.settings.sharedSecret=c,await this.plugin.saveSettings()}));let d=new f.Setting(p).setName("Auth");this.plugin.settings.authToken&&d.setDesc("\u2705 Authorized"),d.addButton(r=>r.setButtonText("Start Auth").onClick(async()=>await this.startAuthProcess())),new f.Setting(p).setName("Default Due Date").setDesc("Setting for new tasks created from editor").addDropdown(r=>r.addOption("none","No Due Date").addOption("today","Today").setValue(this.plugin.settings.defaultDueForNewTask).onChange(async c=>{this.plugin.settings.defaultDueForNewTask=c,await this.plugin.saveSettings()})),new f.Setting(p).setName("Note Creation Folder").setDesc("Folder to create notes from RTM tasks.").addDropdown(r=>{r.addOption("","/ (Vault Root)"),this.app.vault.getAllLoadedFiles().filter(l=>l instanceof f.TFolder).forEach(l=>{l.path!=="/"&&r.addOption(l.path,l.path)}),r.setValue(this.plugin.settings.noteCreationFolder),r.onChange(async l=>{this.plugin.settings.noteCreationFolder=l,await this.plugin.saveSettings()})}),new f.Setting(p).setName("Import with Notes and RTM Link").setDesc("When inserting tasks to the editor, also include RTM task notes and links as sub-items.").addToggle(r=>r.setValue(this.plugin.settings.importWithNotesAndLink).onChange(async c=>{this.plugin.settings.importWithNotesAndLink=c,await this.plugin.saveSettings()}))}async startAuthProcess(){let p=this.plugin,t=p.settings.apiKey,d=p.settings.sharedSecret;if(!t||!d){new f.Notice("Enter keys first");return}let r={method:"rtm.auth.getFrob",api_key:t,format:"json"},c=d;Object.keys(r).sort().forEach(h=>c+=h+r[h]),r.api_sig=I(c);let m=(await(0,f.requestUrl)({url:`${nt}?${new URLSearchParams(r).toString()}`})).json.rsp.frob,g={api_key:t,perms:"delete",frob:m},y=d;Object.keys(g).sort().forEach(h=>y+=h+g[h]);let u=I(y);window.open(`${dt}?api_key=${t}&perms=delete&frob=${m}&api_sig=${u}`),new st(this.app,m,p,async()=>{this.display()}).open()}},st=class extends f.Modal{constructor(p,t,d,r){super(p),this.frob=t,this.plugin=d,this.onSuccess=r}onOpen(){let{contentEl:p}=this;new f.Setting(p).addButton(t=>t.setButtonText("Finish Auth").setCta().onClick(async()=>{let d=this.plugin.settings.apiKey,r=this.plugin.settings.sharedSecret,c={method:"rtm.auth.getToken",api_key:d,format:"json",frob:this.frob},l=r;Object.keys(c).sort().forEach(m=>l+=m+c[m]),c.api_sig=I(l);try{let m=await(0,f.requestUrl)({url:`${nt}?${new URLSearchParams(c).toString()}`});m.json.rsp.auth?(this.plugin.settings.authToken=m.json.rsp.auth.token,await this.plugin.saveSettings(),new f.Notice("Success!"),this.onSuccess(),this.close()):new f.Notice("Failed.")}catch(m){new f.Notice("Error.")}}))}onClose(){this.contentEl.empty()}}; diff --git a/manifest.json b/manifest.json index c0fcb0d..4bf7adb 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "rtm-sync", "name": "RTM Sync", - "version": "1.3.2", + "version": "1.3.3", "minAppVersion": "0.15.0", "description": "Sync Remember The Milk Tasks.", "author": "Osamu Kishiyama", diff --git a/package.json b/package.json index 519e263..967cfb6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rtm-sync", - "version": "1.3.2", + "version": "1.3.3", "description": "Sync Remember The Milk Tasks with Obsidian", "main": "main.js", "type": "module", diff --git a/src/main.ts b/src/main.ts index 8d2fce9..7647b62 100644 --- a/src/main.ts +++ b/src/main.ts @@ -570,7 +570,11 @@ class RtmSettingTab extends PluginSettingTab { new Setting(containerEl).setName('API Key').addText(text => text.setValue(this.plugin.settings.apiKey).onChange(async (v) => { this.plugin.settings.apiKey = v; await this.plugin.saveSettings(); })); new Setting(containerEl).setName('Shared Secret').addText(text => text.setValue(this.plugin.settings.sharedSecret).onChange(async (v) => { this.plugin.settings.sharedSecret = v; await this.plugin.saveSettings(); })); - new Setting(containerEl).setName('Auth').addButton(b => b.setButtonText('Start Auth').onClick(async () => await this.startAuthProcess())); + const authSetting = new Setting(containerEl).setName('Auth'); + if (this.plugin.settings.authToken) { + authSetting.setDesc('✅ Authorized'); + } + authSetting.addButton(b => b.setButtonText('Start Auth').onClick(async () => await this.startAuthProcess())); new Setting(containerEl) .setName('Default Due Date') diff --git a/versions.json b/versions.json index 2e10153..a7dcb72 100644 --- a/versions.json +++ b/versions.json @@ -3,5 +3,6 @@ "1.2.0": "0.15.0", "1.3.0": "0.15.0", "1.3.1": "0.15.0", - "1.3.2": "0.15.0" + "1.3.2": "0.15.0", + "1.3.3": "0.15.0" }