Skip to content

Instantly share code, notes, and snippets.

@uroybd
Last active November 29, 2023 10:38
Show Gist options
  • Save uroybd/c58b3451047eec17107d7f91b4198bb1 to your computer and use it in GitHub Desktop.
Save uroybd/c58b3451047eec17107d7f91b4198bb1 to your computer and use it in GitHub Desktop.
Obsidian Letter to Self.
// Custom JS class to provide me with common formatters. Here's the relevant part:
class Formatters {
format_letter(l, dv, delivered = false) {
const seal = delivered ? '<div class="seal"><div class="seal-text">Delivered</div></div>' : '';
dv.el("a", `<div class="subject">${l.title}</div>
<div class="parts">
<div class="part from">
<div class="title">From,</br>${l.from + ' Self'}</div>
<div class="date">Posted on: ${dv.date(l.created).toFormat("dd MMM yyyy")}</div>
</div>
<div class="part to">
<div class="title">To,</br>${l.from == "Younger" ? 'Older Self' : "Younger Self"}</div>
<div class="date">${l.from == "Younger" && delivered ? 'Received on: ' + dv.date(l.deliver_on).toFormat("dd MMM yyyy") : ""}</div>
</div>
${seal}
</div>`,
{ cls: `internal-link letter-to-self ${l.from.toLowerCase()} ${delivered ? '' : 'undelivered'}`,
attr: { href: l.file.path, target: "_blank"}});
}
}
title aliases created updated
Inbox
Inbox
2023-11-22 14:01:18 +0600
2023-11-23 08:49:57 +0600

Will Be Delivered soon…

// I am using Custom JS plugin to define common JS functions throughout the vault.
const {Formatters} = customJS;
const now = dv.luxon.DateTime.now()
const in_three_days = now.plus({days: 3});

let letters = dv.pages('"Personal/Letters to Self"').where((l) => {
  if (l.from != "Younger") return false;
  const delivery_date = dv.date(l.deliver_on);
  return (delivery_date > now && delivery_date <= in_three_days && !dv.pages('"Personal/Letters to Self"').where((p) => p.from == "Older" && p.replied_to.path == l.file.path).length);
}).sort(l => l.deliver_on, 'desc');

if (!letters.length) {
  dv.paragraph("No letter to be expected soon!")
} else {
  letters.forEach((l) => {
    Formatters.format_letter(l, dv);
  });
}

Yet to Reply

const {Formatters} = customJS;
const now = dv.luxon.DateTime.now();

let letters = dv.pages('"Personal/Letters to Self"').where((l) => {
  return l.from == "Younger" && (dv.date(l.deliver_on) <= now && !dv.pages('"Personal/Letters to Self"').where((p) => p.from == "Older" && p.replied_to.path == l.file.path).length);
}).sort(l => l.deliver_on, 'desc');

if (!letters.length) {
  dv.paragraph("No letter to reply!")
} else {
  letters.forEach((l) => {
    Formatters.format_letter(l, dv, true);
  });
}

Delivered

const {Formatters} = customJS;
const now = dv.luxon.DateTime.now();

let letters = dv.pages('"Personal/Letters to Self"').where((l) => {
  if (!l.from) return false;
  if (l.from == "Older") return true;
  return (dv.date(l.deliver_on) <= now && dv.pages('"Personal/Letters to Self"').where((p) => p.from == "Older" && p.replied_to.path == l.file.path).length)
  }).sort(l => l.from == "Older" ? l.created : l.deliver_on, 'desc');

  if (!letters.length) {
    dv.paragraph("No letter in the Archive!");
  } else {
  letters.forEach((l) => {
    console.log(l);
    Formatters.format_letter(l, dv, true);
  })
}

<%* const path = "Personal/Letters to Self"; const status = await tp.system.suggester(['Letter to Self', 'Reply to Self'], [true, false], true, 'type'); var selectedFile; var title = ""; var deliveryOn; if (!status) { const filteredFiles = app.vault.getMarkdownFiles().filter(file => {
return file.path.startsWith(path); }); selectedFile = (await tp.system.suggester((file) => file.basename, filteredFiles)); await app.fileManager.processFrontMatter(selectedFile, (fm) => { if (fm.title) { title = fm.title } else { title = file.name } }); }

title = await tp.system.prompt("Title", !status ? "Reply to " + title : "");

var additionalData; if (status) { deliverOn = await tp.system.prompt("Delivery On", tp.date.now('YYYY-MM-DD', "P+1M")); additionalData = deliver_on: ${deliverOn} } else { additionalData = replied_to: "[[${selectedFile.path}|${selectedFile.basename}]]" }

const now = tp.date.now()

let frontmatter = `--- title: "${title}" aliases: ["${title}"] tags: ["letter-to-self", "letter-to-self/${status ? 'younger' : 'older'}"] from: "${status ? 'Younger' : 'Older'}" ${additionalData} created: ${now} updated: ${now}

if (!status) { await app.fileManager.processFrontMatter(selectedFile, (fm) => { fm.reply =[[${path}/${title}|${title}]]}); frontmatter +=![[${selectedFile.path}|${selectedFile.basename}]]\n` }

const newFile = await tp.file.create_new(frontmatter, ${title}.md, true, app.vault.getAbstractFileByPath(path)); %>

<%* // This template script assumes that all letters will be laced in "Personal/Letters to Self"; // Plugins being used: Templater, Custom JS, Dataview, Obsidian Linter

const path = "Personal/Letters to Self"; const status = await tp.system.suggester(['Letter to Self', 'Reply to Self'], [true, false], true, 'type'); var selectedFile; var title = ""; var deliveryOn; if (!status) { const filteredFiles = app.vault.getMarkdownFiles().filter(file => {
return file.path.startsWith(path); }); selectedFile = (await tp.system.suggester((file) => file.basename, filteredFiles)); await app.fileManager.processFrontMatter(selectedFile, (fm) => { if (fm.title) { title = fm.title } else { title = file.name } }); }

title = await tp.system.prompt("Title", !status ? "Reply to " + title : "");

var additionalData; if (status) { deliverOn = await tp.system.prompt("Delivery On", tp.date.now('YYYY-MM-DD', "P+1M")); additionalData = deliver_on: ${deliverOn} } else { additionalData = replied_to: "[[${selectedFile.path}|${selectedFile.basename}]]" }

const now = tp.date.now()

const frontmatter = `--- title: "${title}" aliases: ["${title}"] tags: ["letter-to-self", "letter-to-self/${status ? 'younger' : 'older'}"] from: "${status ? 'Younger' : 'Older'}" ${additionalData} created: ${now} updated: ${now}

if (!status) { await app.fileManager.processFrontMatter(selectedFile, (fm) => { fm.reply =[[${path}/${title}|${title}]]` }); }

const newFile = await tp.file.create_new(frontmatter, ${title}.md, true, app.vault.getAbstractFileByPath(path)); %>

/* This is the snippet to put in the .obsidian/snippets path. Enable it to get the nice formatting. */
.letter-to-self {
padding: 20px;
border: 1px solid #ccc;
margin-top: 10px;
border-radius: 3px;
font-family: cursive;
display: block;
color: black !important;
text-decoration: none !important;
position: relative;
}
.letter-to-self.undelivered {
opacity: 0.5 !important;
}
.letter-to-self .seal {
position: absolute;
right: 5px;
top: 5px;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-weight: 900;
text-transform: uppercase;
height: 100px;
width: 100px;
border-radius: 50%;
border: 20px solid rgb(181, 67, 67);
display: flex;
justify-content: center;
align-items: center;
transform: rotateZ(-30deg) scale(0.6);
}
.letter-to-self .seal .seal-text {
background-color: rgb(181, 67, 67);
color: white;
}
.letter-to-self:hover {
text-decoration: none !important;
}
.letter-to-self.younger {
margin-right: 20%;
background-color: antiquewhite;
border: 1px solid antiquewhite;
}
.letter-to-self.older {
margin-left: 20%;
background-color: rgb(251, 247, 242);
border: 1px solid rgb(251, 247, 242);
}
.letter-to-self .subject {
font-weight: bold;
font-size: 1.3em;
margin-bottom: 20px;
}
.letter-to-self .parts {
display: flex;
flex-direction: row;
}
.letter-to-self .part {
padding: 5px;
flex: 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment