Added divided journals

This commit is contained in:
Tomas Rivera
2026-04-03 18:43:49 +02:00
parent 4515378adc
commit 6ac2c71ee4
5 changed files with 95 additions and 12 deletions
+2 -1
View File
@@ -36,7 +36,8 @@ Change `$/.config/notewrapper/config.json`. If it does not exist. On building, i
- [x] Port vivify.vim to nixpkgs
- [ ] Add a way to have vaults in different directories
- [x] Actually open vivify when opening nvim
- [ ] Write the journaling code (separate files or one big journal files)
- [x] Write the journaling code (separate files or one big journal files)
- [ ] Adapt createNewNote with journals
- [ ] Automatic backups
- [x] Add flags for customization
- [ ] Write a good README.md
+4 -4
View File
@@ -210,8 +210,8 @@ int main(int argc, char *argv[]) {
vaultSelected = bypassVaultSelection;
goto note_selection;
}
vaultSelected = ncursesSelect(vaultsArray, "Select vault (Use arrows or WASD, Enter to select):", vaultsCount, extraOptions, shouldDebug);
vaultSelected = ncursesSelect(vaultsArray, "vault", vaultsCount, extraOptions, " ", "Or select an option below", "", shouldDebug);
// now that we won't use vaultsArray in this iteration of the loop, we should free it and all its elements. (As this is memory in the heap and not the stack and thus is our responsability to manage)
for (int i = 0; i < vaultsCount; i++) {
if (vaultSelected != vaultsArray[i]) { // i forgot this condition before. and freed the pointer equal to vaultSelected... So don't remove this condition
@@ -264,7 +264,7 @@ note_selection:
goto note_creation;
}
}
noteSelected = ncursesSelect(filesArray, "Select note or journal (Use arrows or WASD, Enter to select):", filesCount, extraNotesOptions, shouldDebug);
noteSelected = ncursesSelect(filesArray, "note or journal", filesCount, extraNotesOptions, " ", "Or select an option below", "", shouldDebug);
// now that we won't use filesArray in this iteration of the loop, we should free it and all its elements. (As this is memory in the heap and not the stack and thus is our responsability to manage)
for (int i = 0; i < filesCount; i++) {
if (noteSelected != filesArray[i]) { // we must prevent noteSelected to be freed. It will cause a lot of problems
@@ -306,7 +306,7 @@ note_creation:
shouldChangeVault = 1;
} else if (strcmp(noteSelected, "Delete vault") == 0) {
const char *yesNo[] = {"No, go back to note selection.", "Yes."};
char *answer = ncursesSelect((char **)yesNo, "Are you sure you want to delete the entire vault? This can not be undone.", 1, 1, shouldDebug);
char *answer = ncursesSelect((char **)yesNo, "Are you sure you want to delete the entire vault? This can not be undone.", 1, 1, " ", "", "", shouldDebug); // (TODO LATER) This is ugly with Select Are you sure[...]
debug("You answered: %s for deleting the vault %s", answer, vaultSelected);
if (strcmp(answer, "Yes.") == 0) {
// delete the vault after confirmation by the user
+69 -3
View File
@@ -135,7 +135,10 @@ char *updateJournal(char *path, char *journal, char *timeFormat, int shouldDebug
char *date = getFormatedTime(timeFormat, shouldDebug);
debug("Formated time for the journal's entry is %s", date);
char dateWithExtension[PATH_MAX];
snprintf(dateWithExtension, PATH_MAX, "%s.md", date);
sanitize(dateWithExtension);
debug("Sanitized date: %s\n(it might be used later for a file name if the journal is divided)", dateWithExtension);
struct stat metadata;
error(stat(path, &metadata), "program", "stat() failed to get information about %s", path);
if (S_ISREG(metadata.st_mode)) {
@@ -144,10 +147,73 @@ char *updateJournal(char *path, char *journal, char *timeFormat, int shouldDebug
appendToFile(path, date, shouldDebug);
} // if there is an entry do nothing
} else if (S_ISDIR(metadata.st_mode)) {
debug("%s is a divided journal.", path);
debug("%s is a divided journal.", path);
struct dirent *dividedJournalEntry;
// snprintf does not like to have the same variable as input and output so we use a buffer
char temp[PATH_MAX];
snprintf(temp, PATH_MAX, "%s/", path); // appends a /. For safety
strncpy(path, temp, PATH_MAX);
DIR *dividedJournalDirectory = opendir(path);
error(dividedJournalDirectory==NULL, "program", "Could not open directory %s", path);
char **entryArray = NULL; // will contain all the entries of the divided journal
// This time it will be different. We must add "invert" the extraOptions to the options so that the Create new entry in journal is on top
entryArray = realloc(entryArray, sizeof(char*));
char *createEntryMessage = malloc(PATH_MAX);
snprintf(createEntryMessage, PATH_MAX, "Create new entry for the journal %s", journal);
entryArray[0] = createEntryMessage;
size_t entryCount = 1; // we need to count how many dirs there is to always readjust how many memory we alloc
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
// for readdir()
debug("┌------------------------------\n Detected files and dirs from %s:", path);
// iterates over all the entries from the dir
while ((dividedJournalEntry = readdir(dividedJournalDirectory)) != NULL) {
// (TODO LATER) find a way to sort them
altDebug("%s\n", dividedJournalEntry->d_name);
if (dividedJournalEntry->d_name[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s", path, dividedJournalEntry->d_name); // sets the full absolute path to fullPathEntry
struct stat metadataPathEntry;
if (stat(fullPathEntry, &metadataPathEntry) == 0 && S_ISREG(metadataPathEntry.st_mode)) { // if this entry is a directory
entryArray = realloc(entryArray, (entryCount + 1)*sizeof(char*));
entryArray[entryCount] = strdup(dividedJournalEntry->d_name); // copy the dir name into entryArray
entryCount++;
}
}
}
altDebug("└------------------------------\n");
// free's some used memory
closedir(dividedJournalDirectory);
// we must now select to create new entry or to enter in old one
char *selectedOption = ncursesSelect(entryArray, "Create new entry or acces old entry:", 1, entryCount -1, " ", " ", "", shouldDebug); // the "Create new entry for the journal %s" will be the only options. All other will be extraOptions. This is made so that "Create [...] %s" will always be on top
debug("Selected option from journal entry selection: %s", selectedOption);
if (strcmp(selectedOption, createEntryMessage) == 0) { // create new entry
char temp[PATH_MAX];
error(strlen(path)+1+strlen(dateWithExtension)+1>PATH_MAX, "Error file path too long. %s/%s must not exceed PATH_MAX", path, dateWithExtension);
snprintf(temp, PATH_MAX, "%s/%s", path, dateWithExtension);
strncpy(path, temp, PATH_MAX);
if (!isStringInArray(date, (const char **)entryArray, entryCount)) { // it only creates it if it doesn't already exist
// if it does exist it will just pass the full path
debug("Creating entry %s inside %s", date, path);
FILE *file;
file = fopen(path, "w");
error(file == NULL, "program", "%s couldn't be created", path);
debug("Writing %s\\n to %s", date, path);
fprintf(file, "%s\n", date);
fclose(file);
}
} else { // we just recreate the path to the selected entry
// snprintf does not like to have the same variable as input and output so we use a buffer
char temp[PATH_MAX];
snprintf(temp, PATH_MAX, "%s/%s", path, selectedOption);
strncpy(path, temp, PATH_MAX);
debug("Returning path to the selected entry: %s", path);
}
// (TODO LATER) free all unused entryArray[i]
} else {
error(1, "program", "%s is not a file and not a directory.", path);
}
return path;
}
+15 -3
View File
@@ -87,7 +87,7 @@ char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, char *bypass,
return fileFullPath;
}
char* ncursesSelect(char **options, char *optionsText, size_t optionsNumber, size_t extraOptionsNumber, int shouldDebug) {
char* ncursesSelect(char **options, char *optionsText, size_t optionsNumber, size_t extraOptionsNumber, char *bottomText, char *middleText, char *topText, int shouldDebug) {
int highlight = 0; //curently highlighted option
int key;
@@ -104,29 +104,41 @@ char* ncursesSelect(char **options, char *optionsText, size_t optionsNumber, siz
clear();
attron(COLOR_PAIR(1));
mvprintw(0,0, "Select %s (Use arrows or WASD, Enter to select):", optionsText);
int offset = 1;
if (strcmp(bottomText, "") != 0) {
mvprintw(offset, 1, "%s", bottomText);
offset++;
}
// Print options with highlighting
for (int i = 0; i < optionsNumber; i++) {
if (i == highlight) {
attron(A_REVERSE);
} // highlight selected
mvprintw(i+2, 2, "%s", options[i]);
mvprintw(i+offset, 2, "%s", options[i]);
if (i == highlight) {
attroff(A_REVERSE);
}
}
attroff(COLOR_PAIR(1));
if (strcmp(middleText, "") != 0) {
mvprintw(offset+optionsNumber, 1, "%s", middleText);
offset++;
}
// Printf extraOptions with highlighting
attron(COLOR_PAIR(2));
for (int k = optionsNumber; k < optionsNumber + extraOptionsNumber; k++) {
if (k == highlight) {
attron(A_REVERSE);
}
mvprintw(k+2, 2, "%s", options[k]);
mvprintw(k+offset, 2, "%s", options[k]);
if (k == highlight) {
attroff(A_REVERSE);
}
}
attroff(COLOR_PAIR(2));
attron(COLOR_PAIR(1));
mvprintw(offset+optionsNumber+extraOptionsNumber, 1, "%s", topText);
attroff(COLOR_PAIR(1));
key = getch(); //get some int which value correspond to some key being pressed
switch(key) {
+5 -1
View File
@@ -20,7 +20,7 @@ void createNewVault(char *dirToVault, int debug);
// Creates a new vault with this input
// If vault already exists, prints a warning
// returns nothing
char* ncursesSelect(char **options, char *optionsText, size_t optionsNumber, size_t extraOptionsNumber, int debug);
char* ncursesSelect(char **options, char *optionsText, size_t optionsNumber, size_t extraOptionsNumber, char *bottomText, char *middleText, char *topText, int debug);
// this function is used multiple times let the user select one options from many with ncurses in a TUI.
// options is the array of strings with all the options.
// optionsText is the text that will be printed at the top (For example: "Please select ...")
@@ -28,5 +28,9 @@ char* ncursesSelect(char **options, char *optionsText, size_t optionsNumber, siz
// options could be the list of all notes or all vaults
// extraOptions are options that are special and have a special color (for ex: "Delete vault", "Settings", etc.)
// note: extraOptions should be at the end of options
// topText is printed between options text and the start of options
// middleText (usally \n) is printed between options and extraOptions
// bottomText is printed bellow
// topText and middle must be exactly one line. If you want empty lines use " " and not ""
// returns the selected option
#endif