#!/bin/sh set -eu # TODO: Make sensitive to failures. I don't want to miss appointments! # TODO Ensure safe use of delimiters err() { echo "❌ $1" >/dev/tty } if [ -z "${FZF_VCAL_USE_EXPORTED:-}" ]; then # Read configuration CONFIGFILE="$HOME/.config/fzf-vcal/config" if [ ! -f "$CONFIGFILE" ]; then err "Configuration '$CONFIGFILE' not found." exit 1 fi # shellcheck source=/dev/null . "$CONFIGFILE" if [ -z "${ROOT:-}" ] || [ -z "${SYNC_CMD:-}" ] || [ -z "${COLLECTION_LABELS:-}" ]; then err "Configuration is incomplete." exit 1 fi export ROOT export SYNC_CMD export COLLECTION_LABELS # Tools if command -v "fzf" >/dev/null; then FZF="fzf" else err "Did not find the command-line fuzzy finder fzf." exit 1 fi export FZF if command -v "uuidgen" >/dev/null; then UUIDGEN="uuidgen" else err "Did not find the uuidgen command." exit 1 fi export UUIDGEN if command -v "bat" >/dev/null; then CAT="bat" elif command -v "batcat" >/dev/null; then CAT="batcat" fi CAT=${CAT:+$CAT --color=always --style=numbers --language=md} CAT=${CAT:-cat} export CAT ### AWK SCRIPTS AWK_LINES=$( cat <<'EOF' @@include src/awk/lines.awk EOF ) export AWK_LINES AWK_MERGE=$( cat <<'EOF' @@include src/awk/merge.awk EOF ) export AWK_MERGE AWK_PARSE=$( cat <<'EOF' @@include src/awk/parse.awk EOF ) export AWK_PARSE AWK_WEEKVIEW=$( cat <<'EOF' @@include src/awk/weekview.awk EOF ) export AWK_WEEKVIEW AWK_DAYVIEW=$( cat <<'EOF' @@include src/awk/dayview.awk EOF ) export AWK_DAYVIEW ### END OF AWK SCRIPTS FZF_VJOUR_USE_EXPORTED="yes" export FZF_VJOUR_USE_EXPORTED fi __load_approx_data() { find "$ROOT" -type f -name '*.ics' -print0 | xargs -0 -P0 \ awk \ -v collection_labels="$COLLECTION_LABELS" \ "$AWK_LINES" } __load_weeks() { dates=$(awk -F'|' '{ print $2; print $3 }' "$APPROX_DATA_FILE") file_dates=$(mktemp) echo "$dates" | date --file="/dev/stdin" +"%s" >"$file_dates" awk "$AWK_MERGE" "$file_dates" "$APPROX_DATA_FILE" rm "$file_dates" } __show_day() { weeknr=$(date -d "$DISPLAY_DATE" +"%s") weeknr=$(((weeknr - 259200) / 604800)) # shift, because epoch origin is a Thursday files=$(grep "^$weeknr " "$WEEKLY_DATA_FILE" | cut -d " " -f 2-) # Find relevant files in list of week files sef=$({ set -- $files for file in "$@"; do file="$ROOT/$file" awk \ -v collection_labels="$COLLECTION_LABELS" \ "$AWK_PARSE" "$file" done }) if [ -n "$sef" ]; then today=$(date -d "$DISPLAY_DATE" +"%D") sef=$(echo "$sef" | while IFS= read -r line; do set -- $line starttime="$1" shift endtime="$1" shift fpath="$1" shift description=$(echo "$*" | sed 's/|/:/g') # we will use | as delimiter # daystart=$(date -d "$today 00:00:00" +"%s") dayend=$(date -d "$today 23:59:59" +"%s") line="" if [ "$starttime" -gt "$daystart" ] && [ "$starttime" -lt "$dayend" ]; then s=$(date -d "@$starttime" +"%H:%M") elif [ "$starttime" -le "$daystart" ] && [ "$endtime" -gt "$daystart" ]; then s="00:00" else continue fi if [ "$endtime" -gt "$daystart" ] && [ "$endtime" -lt "$dayend" ]; then e=$(date -d "@$endtime" +"%H:%M") elif [ "$endtime" -ge "$dayend" ] && [ "$starttime" -lt "$dayend" ]; then e="00:00" else continue fi echo "$s|$e|$fpath|$description" done) fi GREEN="\033[1;32m" OFF="\033[m" echo "|🗓️ $GREEN $(date -d "$DISPLAY_DATE" +"%a %e %b %Y")$OFF" echo "" ( echo "$sef" seq 8 18 | sort -n ) | awk "$AWK_DAYVIEW" } __list() { weeknr=$(date -d "$DISPLAY_DATE" +"%s") weeknr=$(((weeknr - 259200) / 604800)) # shift, because epoch origin is a Thursday files=$(grep "^$weeknr " "$WEEKLY_DATA_FILE" | cut -d " " -f 2-) dayofweek=$(date -d "$DISPLAY_DATE" +"%u") delta=$((1 - dayofweek)) startofweek=$(date -d "$DISPLAY_DATE -$delta days" +"%D") # loop over files sef=$({ set -- $files for file in "$@"; do file="$ROOT/$file" awk \ -v collection_labels="$COLLECTION_LABELS" \ "$AWK_PARSE" "$file" done }) if [ -n "$sef" ]; then sef=$(echo "$sef" | while IFS= read -r line; do set -- $line starttime="$1" shift endtime="$1" shift #fpath="$1" shift description="$*" for i in $(seq 0 7); do daystart=$(date -d "$startofweek +$i days 00:00:00" +"%s") dayend=$(date -d "$startofweek +$i days 23:59:59" +"%s") if [ "$starttime" -gt "$daystart" ] && [ "$starttime" -lt "$dayend" ]; then s=$(date -d "@$starttime" +"%H:%M") s="$s -" elif [ "$starttime" -le "$daystart" ] && [ "$endtime" -gt "$daystart" ]; then s="00:00 -" else continue fi if [ "$endtime" -gt "$daystart" ] && [ "$endtime" -lt "$dayend" ]; then e=$(date -d "@$endtime" +"%H:%M") e="- $e" elif [ "$endtime" -ge "$dayend" ] && [ "$starttime" -lt "$dayend" ]; then e="- 00:00" else continue fi echo "$i $s$e >$description" done done) fi sef=$({ echo "$sef" seq 0 7 } | sort -n) echo "$sef" | awk -v startofweek="$startofweek" "$AWK_WEEKVIEW" #seq -f "$startofweek +%g days" 0 6 | # LC_ALL=c xargs -I {} date -d "{}" +"%a %e %b %Y" } if [ -z "${APPROX_DATA_FILE:-}" ]; then APPROX_DATA_FILE=$(mktemp) __load_approx_data >"$APPROX_DATA_FILE" export APPROX_DATA_FILE fi if [ -z "${WEEKLY_DATA_FILE:-}" ]; then WEEKLY_DATA_FILE=$(mktemp) __load_weeks >"$WEEKLY_DATA_FILE" export WEEKLY_DATA_FILE fi if [ "${1:-}" = "--day" ]; then DISPLAY_DATE="$2" echo "Jumping to day $DISPLAY_DATE!" selection=$( __show_day | $FZF \ --reverse \ --ansi \ --no-sort \ --no-input \ --delimiter='|' \ --with-nth='{2}' \ --accept-nth='{1}' \ --bind="backspace:first+accept" ) if [ -n "$selection" ]; then fpath="$ROOT/$selection" fi fi if [ "${1:-}" = "--date" ]; then DISPLAY_DATE="$2" echo "Jumping to date $2!" fi DISPLAY_DATE=${DISPLAY_DATE:-today} DISPLAY_DATE=$(date -d "$DISPLAY_DATE" +"%D") DISPLAY_DATE_PREV=$(date -d "$DISPLAY_DATE -1 week" +"%D") DISPLAY_DATE_NEXT=$(date -d "$DISPLAY_DATE +1 week" +"%D") selection=$( ( cat "$APPROX_DATA_FILE" yes " " | head -n 50 __list ) | $FZF \ --tac \ --no-sort \ --no-hscroll \ --ellipsis='' \ --delimiter='|' \ --with-nth='{4}' \ --accept-nth=1,2 \ --no-info \ --ansi \ --no-clear \ --bind="ctrl-p:become($0 --date '$DISPLAY_DATE_PREV')" \ --bind="ctrl-n:become($0 --date '$DISPLAY_DATE_NEXT')" \ --bind="ctrl-l:become($0)") if [ -z "$selection" ]; then rm "$WEEKLY_DATA_FILE" "$APPROX_DATA_FILE" return 0 fi sign=$(echo "$selection" | cut -d '|' -f 1) startdate=$(echo "$selection" | cut -d '|' -f 2) if [ "$sign" = "~" ]; then exec $0 --date "$startdate" else exec $0 --day "$startdate" fi echo "Going to end..." echo "$selection" echo "STOPPING NOW"