dvargas92495 / SmartBlocks

Useful examples from developer community for Roam42 SmartBlocks
146 stars 6 forks source link

Timestamp button for interstitial journaling + Elapsed time calculator v0.22 #219

Open fbgallet opened 3 years ago

fbgallet commented 3 years ago

Version 0.22, June 1st, 2021 Download JSON file (.zip to extract, then import JSON file in your graph) to better preserve the structure of the code: Timestamp button for interstitial journaling v0.22.zip

πŸ“‹ Description: simple Timestamp buttons

Really simple SmartBlocks duplicating the timestamp button at each use, and properly positioning the cursor to write straight away.

You need to add the first button in you daily template (or create one), see image below. Just copy/past this: {{πŸ•—β†¦:42SmartBlock:Time interval button}} where "Time interval button" is the name of the SmartBlock to run here (change to "Time button" if you want the "only beginning time" version, or "Time interval button + elapsed time" if you want an automatic calcul of elapsed time (see next section)). image

You can change the time format from <%TIME%> to <%TIMEAMPM%> if you prefer (don't work actually with Elapsed time SmartBlock).

πŸ“‹ Description: Elapsed time calculator (included from v0.2)

Copy/Paste code below in your graph, or (more reliable) import JSON file. It should be used in combination with 'Timestamp button + elapsed time' SmartBlock and button {{πŸ•—β†¦:42SmartBlock:Time interval button + elapsed time}} added in your daily template.

The SmartBlock calculates the time elapsed between to timestamps (minutes, no more than 1440).

Then it test if it's less or more than a given limit. There is two kind of limits:

Limits are linked to trigger words, searched in current block and first children block (case insensitive). There is 3 ways to set trigger words:

If there is no trigger word in the block, default time limit is used (see user settings in JS block). Default is 60' (it means that the popup notification appears if elapsed time is more than 60'). Set it to 0 if you want the popup every time.

By default, if there is some trigger word, a popup notification will appear, and you have to confirm for applying formatting (if it's less, or more, or exactly the elapsed time required), if not the popup desappear after 6 seconds. There is a user setting for desactivating this notification and automatic apply the formatting. Nb: you can use keyboard to confirm: the focus is automatically set on the right button, you have just to press Enter to apply formatting.

Of course, formatting (that is a string inserted in the current block, after the elapsed time) can set by user, also in the JS code. By default it's using the css code below (block background color is set to red or green), but you can just insert a word or an icon, or nothing.

This video shows the main features

πŸ” Change log

v0.22 (June 1st, 2021)

βœ… Prerequisites for this SmartBlock

Roam42 must be installed (instructions here).

For formating the blocks depending on the elapsed time, you can use this css (very simple example) (it's included in the JSON file):

.exceeded-time,
.insufficient-time {
    background-color: #f8d7da !important;
}

.good-time {
    background-color: #D2FFD4 !important;
}

πŸ“· Screenshot

image (Content of the code block doesn't appear entirely in the screenshot below) image

πŸ’‘ Additional Info

Download JSON file (.zip to extract, then import JSON file in your graph) to better preserve the structure of the code: Timestamp button for interstitial journaling v0.22.zip

βœ‚οΈ #42SmartBlock code:

Copy/Paste the code below anywhere in your graph (for example in the page 'SmartBlocks'). Proceed in two times: first, the Smartblocks, then the Javascript code, to insert in the code block of the last Smartblock (if you are not sure, you should better import the JSON file):

⚠Copy/paste the code below in the code block in the Elapsed time Smartblock (remove 'javascript' at the top of the code):

/******************************************************************
        Elapsed time calculator between two timestamps

Version: 0.22
Published: June 1st, 2021
By: Fabrice Gallet
Twitter: @fbgallet

Support my work on: https://www.buymeacoffee.com/fbgallet

/************************* USER SETTINGS **************************/
  let defaultTimeLimit = 60;     // set to 0 if you want always popup notification
  let limitPresets = true;       // false if you want disable trigger words search
                                 // change or add as many limit and trigger as you want
  let minLimitPresets = [        // search is case insensitive
          {limit: 15, trigger: ["Meditation", "Morning page"]},
          {limit: 30, trigger: ["read"]},
          {limit:   45, trigger: ["Deep work"]}  // add a ',' here if you add a new limit
      ];
  let maxLimitPresets = [
          {limit: 15, trigger: ["pause", "coffee", "call", "browsing", "twitter"]},
          {limit:   40, trigger: ["coding", "revue"]}
      ];
  let inlineMinLimit = "goal:"; 
  let inlineMaxLimit = "max:";
  let includeFirstChild = true;   // search for trigger words in current block AND first child block
  let pomoIsLimit = true;         // Pomodotor timer as min trigger
  let confirmPopup = true;        // false if you want automatic formating without popup notification
  let blockExcessFormat = "#[[.exceeded-time]] "; // πŸ›‘ it can be just an icon like this, or a word
  let blockNotEnoughFormat = "#[[.insufficient-time]] "; // πŸ”‚
  let blockGoodFormat = "#[[.good-time]] "; // βœ…
  let intervalSeparator = " β‡₯ ";  // serapator used between the two timestamps
  let appendHourTag = false;      // add a tag with the round current hour, like #19:00
/**********************************************************************/

let blockUID = roam42.common.currentActiveBlockUID();
let blockContent = document.activeElement.value;
let blockSplit = blockContent.split(":");
let b = blockSplit[0].length - 2;
if (b < 0) { b = 0;}
let beginH = parseInt(blockContent.slice(b+0,b+2));
let beginM = parseInt(blockContent.slice(b+3,b+5));
let beginT = blockContent.slice(b+0,b+5);

let currentH, currentM, currentT;
let title="";
let withPomo = false;

if (blockContent.search(intervalSeparator) == b+5) {
  let l = intervalSeparator.length;
  currentH = parseInt(blockContent.slice(b+5+l,b+7+l));
  currentM = parseInt(blockContent.slice(b+8+l,b+10+l));
  currentT = blockContent.slice(b+5+l,b+10+l);
  title = blockContent.slice(b+10+l);
}
else {
  let current = new Date();
  currentH = current.getHours();
  currentM = current.getMinutes();
  currentT = addZero(currentH) + ":" + addZero(currentM);
  title = blockContent.slice(b+5);
}

let elapsedH = currentH - beginH;
if (elapsedH < 0) {elapsedH = 24 - beginH + currentH; }
let elapsedM = currentM - beginM;
let elapsedT = (elapsedH * 60) + elapsedM;

let hourTag = "";
if (appendHourTag) { hourTag = " #[[" + beginH + ":00]]"; }

let newContent = blockSplit[0].slice(0, -2) + beginT + intervalSeparator + currentT + " "   + "(**" + elapsedT + "'**) ";
let titleForSearch = title;
// include fisrt children block for searching trigger words
if (includeFirstChild) {
  let blockTree = await roam42.common.getBlockInfoByUID(blockUID, true);
  if (blockTree[0][0].children) { 
    titleForSearch += " " + blockTree[0][0].children[0].string;
  }
}

let withMin = false;
let withMax = false;
let minIndex = titleForSearch.search(new RegExp(inlineMinLimit, "i"));
let maxIndex = titleForSearch.search(new RegExp(inlineMaxLimit, "i"));
let timeLimitMin=0, timeLimitMax=1000;

if (minIndex != -1) {
  withMin = true;
  timeLimitMin = extractLimit(titleForSearch.slice(minIndex), inlineMinLimit.length);
} 
if (maxIndex != -1) {
  withMax = true;
  timeLimitMax = extractLimit(titleForSearch.slice(maxIndex), inlineMaxLimit.length);
}

  if (pomoIsLimit) {
    let indexPomo = title.search("POMO");
    if (indexPomo != -1) {
      timeLimitMax = extractLimit(titleForSearch.slice(indexPomo), 8);
      withMax = true;
      withPomo = true;
    }
  }

  if (limitPresets) {
    let indexLimit = -1;
    let end = maxLimitPresets.length;
    if (!withMax || withPomo) {
      for (let i=0; i < end; i++) {
        let nbTriggers = maxLimitPresets[i].trigger.length;
        for (let j=0; j < nbTriggers; j++) {
          indexLimit = titleForSearch.search(new RegExp(maxLimitPresets[i].trigger[j], "i"));
          if (indexLimit != -1) {
            timeLimitMax = maxLimitPresets[i].limit;
            withMax = true;
            break;
          }
        }
        if (withMax) { break; }
      } 
    }
    if (!withMin) {       
      end = minLimitPresets.length;
      for (let i=0; i < end; i++) {
        let nbTriggers = minLimitPresets[i].trigger.length;
        for (let j=0; j < nbTriggers; j++) {
          indexLimit = titleForSearch.search(new RegExp(minLimitPresets[i].trigger[j], "i"));
          if (indexLimit != -1) {
            timeLimitMin = minLimitPresets[i].limit;
            withMin = true;
            break;
          }
        }
        if (withMin) { break; }
      }
    }
  }

  let triggered = withMax || withMin || withPomo;
  if (withMax && withMin && (timeLimitMax <= timeLimitMin)) { withMin = false; }
  if (!triggered) {timeLimitMax = defaultTimeLimit;}
  let exceeded = ((withMax || !triggered) && (elapsedT > timeLimitMax));
  let insufficient = (withMin && (elapsedT < timeLimitMin));
  let okUnder = !exceeded && !withMin && triggered;
  let okOver = !insufficient && !withMax && triggered; 

if (exceeded || insufficient || okUnder || okOver || triggered) { 
    let textTitle = elapsedT + "' elapsed.";
    let textMessage = "Goal was "; // " fixed to: " + timeLimit + "'. This is:";
    let buttonCaption = "Too much anyway!";
    let badFormat = blockExcessFormat;

    if ((!exceeded && !insufficient) || (okUnder && !insufficient) || (okOver && !exceeded)) {
      if ((!okOver && !okUnder)) { 
        textMessage += "between " + timeLimitMin + "' & " + timeLimitMax + "'";
     } else if (okUnder) {
        textMessage += "less than " + timeLimitMax + "'";
        badFormat = blockNotEnoughFormat;
      } else {
        textMessage += "more than " + timeLimitMin + "'";
        buttonCaption = "Not enough anyway!";
      }
      if (confirmPopup) {
        buttonCaption = "<button>" + buttonCaption + "</button>";
        iziToast.success({
          timeout: 6000, displayMode: 'replace', id: 'timing', zindex: 999, 
          title: textTitle,
          message: textMessage,
          position: 'bottomCenter',  drag: false, close:true,
          buttons: [
                      ['<button>Great!</button>', (instance, toast)=> {
                          roam42.common.updateBlock(blockUID, newContent + blockGoodFormat + title.trim() + hourTag, true);

                      instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
                      }, true],
                      [buttonCaption, (instance, toast)=> {
                              roam42.common.updateBlock(blockUID, newContent + badFormat + title.trim() + hourTag, true);
                      instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
                      }]
                   ], 
       });
      await roam42.common.sleep(150);
      await roam42KeyboardLib.pressEsc(150);
      }
    }
    else {
      if ((exceeded && withMin) || (insufficient && withMax)) { 
        textMessage += "between " + timeLimitMin + "' & " + timeLimitMax + "'";
        buttonCaption = "Too much!";
        if (insufficient) { 
          badFormat = blockNotEnoughFormat;
          buttonCaption = "Not enough!";
        }
     } else if ((!okUnder && !insufficient)  || exceeded) {
        buttonCaption = "Too much!";
       if (!triggered) { 
          textMessage = "Default alert time is " + timeLimitMax + "'";}
        else {textMessage += "less than " + timeLimitMax + "'";}
      } else {
        if (!triggered) { 
         textMessage = "Default alert time is " + timeLimitMax + " '.";}
        else { textMessage += "more than " + timeLimitMin + " '."; }
        buttonCaption = "Not enough!"
        badFormat = blockNotEnoughFormat;
      }

    if (confirmPopup) {
      buttonCaption = "<button>" + buttonCaption + "</button>";
      iziToast.warning({
          timeout: 6000, displayMode: 'replace', id: 'timing', zindex: 999, 
          title: textTitle,
          message: textMessage,
          position: 'bottomCenter',  drag: false, close:true,
          buttons: [
                      ['<button>Good anyway!</button>', (instance, toast)=> {
                          roam42.common.updateBlock(blockUID, newContent + blockGoodFormat + title.trim() + hourTag, true);
                      instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
                      }],
                      [buttonCaption, (instance, toast)=> {
                              roam42.common.updateBlock(blockUID, newContent + badFormat + title.trim() + hourTag, true);
                      instance.hide({ transitionOut: 'fadeOut' }, toast, 'button');
                      }, true]
                   ], 
       });
      await roam42.common.sleep(150);
      await roam42KeyboardLib.pressEsc(150);
    }
    else {
      newContent = newContent + badFormat;
    }
  } 
}
  await roam42.common.updateBlock(blockUID, newContent + title.trim() + hourTag, true);
  await roam42.common.sleep(150);
  await roam42KeyboardLib.pressEsc(150);

function extractLimit (s, shift) {
  let t = "";
  let i = 0;
  while ((i+shift < s.length) && (!isNaN(s.charAt(i+shift)))) {
    t += s.charAt(i+shift);
    i++;
  }
  return t;
}

function addZero(i) {
  if (i < 10) {
    i = "0" + i;
  }
  return i;
}
edshamis commented 3 years ago

Thanks! Just what is needed!

MaskyS commented 3 years ago

@fbgallet very nice smartblock! Quick suggestion, you can now nest a css code block under {{[[roam/css]]}} and it'll run.

nanjade commented 3 years ago

This plug-in is not work at SmartBlocks V2, is there any update for SBv2?

fbgallet commented 3 years ago

This plug-in is not work at SmartBlocks V2, is there any update for SBv2?

Uptade for SBv2 is published here: https://roamresearch.com/?server-port=3333#/app/Roam-En-Francais/page/-9fGz51_v

helenysli commented 3 years ago

@fbgallet I've very much enjoyed the timestamped button. But the Elapsed Time in my Roam has not been working properly for two days. It said "Block threw an error while running". I've updated to SBv2. Could you tell me what I should do to have it fixed? Thanks!

fbgallet commented 3 years ago

@fbgallet I've very much enjoyed the timestamped button. But the Elapsed Time in my Roam has not been working properly for two days. It said "Block threw an error while running". I've updated to SBv2. Could you tell me what I should do to have it fixed? Thanks!

Hi @helenysli , have you installed the Sb v2 compatible version from the SmartBlock Store (Cmd/Ctrl + p, then search for "store") ? For the current version, you need also Roam42 installed in your graph.

fbgallet commented 3 years ago

Be sure, before installing the new version, to remove the previous one from your graph. Once the Sb will we installed from the store, the updates will be more easy (one click on "Update" button in the store).

helenysli commented 3 years ago

@fbgallet It worked! It turned out that I hadn't actually updated to SBv2. Thank you very much for the great work. It does make my life easier.