Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/vault/demo-tour.json

Large diffs are not rendered by default.

66 changes: 61 additions & 5 deletions internal/vault/demo.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import (
_ "embed"
"encoding/json"
"os"
"path"
"path/filepath"
"regexp"
"sort"
"strings"
)

Expand All @@ -27,6 +30,47 @@ type DemoTourResult struct {
AssetPaths []string `json:"assetPaths"`
}

// olderDemoTourNote matches a tour note under the name the tour used until
// 2.68.0, "NN <long dash> Title.md" (U+2014); it is now "NN Title.md". Vaults
// seeded back then still hold those files. The match is exact on that
// separator because regenerating and removing the tour delete what it matches.
// A synced copy of currentDemoTourPath in the desktop's
// packages/shared-domain/src/demo-tour.ts.
var olderDemoTourNote = regexp.MustCompile(`^(\d{2}b?) \x{2014} (.+\.md)$`)

// olderDemoTourNotePaths lists the tour's notes still on disk under their
// older names, as vault-relative slash paths.
func (v *Vault) olderDemoTourNotePaths(tour *demoTour) []string {
current := map[string]bool{}
dirs := map[string]bool{}
for _, note := range tour.Notes {
current[note.Path] = true
dirs[path.Dir(note.Path)] = true
}
older := []string{}
for dir := range dirs {
abs, err := SafeJoin(v.root, dir)
if err != nil {
continue
}
entries, err := os.ReadDir(abs)
if err != nil {
continue
}
for _, entry := range entries {
if entry.IsDir() {
continue
}
m := olderDemoTourNote.FindStringSubmatch(entry.Name())
if m != nil && current[dir+"/"+m[1]+" "+m[2]] {
older = append(older, dir+"/"+entry.Name())
}
}
}
sort.Strings(older)
return older
}

func loadDemoTour() (*demoTour, error) {
tour := &demoTour{}
if err := json.Unmarshal(demoTourJSON, tour); err != nil {
Expand All @@ -36,14 +80,20 @@ func loadDemoTour() (*demoTour, error) {
}

// GenerateDemoTour seeds the vault with the built-in tour notes and
// the demo attachment. Existing files are overwritten.
// the demo attachment. Existing files are overwritten, and a tour seeded
// under the older note names is replaced rather than left beside the new one.
func (v *Vault) GenerateDemoTour() (DemoTourResult, error) {
v.mu.Lock()
defer v.mu.Unlock()
tour, err := loadDemoTour()
if err != nil {
return DemoTourResult{}, err
}
for _, rel := range v.olderDemoTourNotePaths(tour) {
if abs, err := SafeJoin(v.root, rel); err == nil {
_ = os.Remove(abs)
}
}
result := DemoTourResult{NotePaths: []string{}, AssetPaths: []string{}}
for _, note := range tour.Notes {
abs, err := SafeJoin(v.root, note.Path)
Expand Down Expand Up @@ -74,8 +124,9 @@ func (v *Vault) GenerateDemoTour() (DemoTourResult, error) {
return result, nil
}

// RemoveDemoTour deletes the demo notes + asset if they exist. Also
// removes empty parent directories under inbox/demo.
// RemoveDemoTour deletes the demo notes + asset if they exist, including
// notes still under the older names. Also removes empty parent directories
// under inbox/demo.
func (v *Vault) RemoveDemoTour() (DemoTourResult, error) {
v.mu.Lock()
defer v.mu.Unlock()
Expand All @@ -85,13 +136,18 @@ func (v *Vault) RemoveDemoTour() (DemoTourResult, error) {
}
result := DemoTourResult{NotePaths: []string{}, AssetPaths: []string{}}
removedDirs := map[string]bool{}
notePaths := []string{}
for _, note := range tour.Notes {
abs, err := SafeJoin(v.root, note.Path)
notePaths = append(notePaths, note.Path)
}
notePaths = append(notePaths, v.olderDemoTourNotePaths(tour)...)
for _, rel := range notePaths {
abs, err := SafeJoin(v.root, rel)
if err != nil {
continue
}
if err := os.Remove(abs); err == nil {
result.NotePaths = append(result.NotePaths, filepath.ToSlash(note.Path))
result.NotePaths = append(result.NotePaths, filepath.ToSlash(rel))
removedDirs[filepath.Dir(abs)] = true
}
}
Expand Down
102 changes: 102 additions & 0 deletions internal/vault/demo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package vault

import (
"os"
"path/filepath"
"sort"
"strings"
"testing"
)

// The tour named its notes "NN <long dash> Title" until 2.68.0. Built from an
// escape so this file never holds the character itself.
const longDash = "\u2014"

func demoNames(t *testing.T, root string) []string {
t.Helper()
entries, err := os.ReadDir(filepath.Join(root, "inbox", "demo"))
if err != nil {
t.Fatal(err)
}
names := []string{}
for _, e := range entries {
names = append(names, e.Name())
}
sort.Strings(names)
return names
}

func vaultWithOlderTour(t *testing.T) (*Vault, string) {
t.Helper()
root := t.TempDir()
v, err := New(root, Options{})
if err != nil {
t.Fatal(err)
}
for _, rel := range []string{
"inbox/demo/00 " + longDash + " Start Here.md",
"inbox/demo/05b " + longDash + " Math Diagrams.md",
"inbox/demo/My own note.md",
// Someone's own note: only the old separator marks a tour note.
"inbox/demo/00 \u00b7 Start Here.md",
} {
writeAsset(t, root, rel, "older\n")
}
return v, root
}

func TestDemoTourNamesCarryNoLongDash(t *testing.T) {
tour, err := loadDemoTour()
if err != nil {
t.Fatal(err)
}
for _, f := range append(tour.Notes, tour.Assets...) {
if strings.Contains(f.Path, longDash) || strings.Contains(f.Body, longDash) {
t.Errorf("%s still holds the long dash", f.Path)
}
}
}

func TestGenerateDemoTourReplacesOlderNames(t *testing.T) {
v, root := vaultWithOlderTour(t)
if _, err := v.GenerateDemoTour(); err != nil {
t.Fatal(err)
}
names := demoNames(t, root)
for _, name := range names {
if strings.Contains(name, longDash) {
t.Errorf("older tour note left beside the new one: %q", name)
}
}
for _, want := range []string{"00 Start Here.md", "05b Math Diagrams.md", "My own note.md", "00 \u00b7 Start Here.md"} {
if !contains(names, want) {
t.Errorf("missing %q in %v", want, names)
}
}
}

func TestRemoveDemoTourRemovesOlderNames(t *testing.T) {
v, root := vaultWithOlderTour(t)
result, err := v.RemoveDemoTour()
if err != nil {
t.Fatal(err)
}
for _, want := range []string{"inbox/demo/00 " + longDash + " Start Here.md", "inbox/demo/05b " + longDash + " Math Diagrams.md"} {
if !contains(result.NotePaths, want) {
t.Errorf("result misses %q: %v", want, result.NotePaths)
}
}
names := demoNames(t, root)
if want := []string{"00 \u00b7 Start Here.md", "My own note.md"}; strings.Join(names, "|") != strings.Join(want, "|") {
t.Errorf("left %v, want %v", names, want)
}
}

func contains(list []string, s string) bool {
for _, x := range list {
if x == s {
return true
}
}
return false
}
2 changes: 1 addition & 1 deletion release.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"version": "2.67.0",
"version": "2.68.0",
"vendorHash": "sha256-ZdOHC2JldvnKSDUFnBUJrKD4F1IWfvYJBksgeDnU9cw="
}
Loading
Loading