import { Viewer } from "@toast-ui/vue-editor";
import api from "../api";
+import * as constants from "../constants";
import { Note, SearchResult } from "./classes";
import EventBus from "../eventBus";
+import * as helpers from "../helpers";
export default {
components: {
data: function() {
return {
- loggedIn: false,
+ views: {
+ login: 0,
+ home: 1,
+ note: 2,
+ search: 3,
+ },
+ currentView: 1,
usernameInput: null,
passwordInput: null,
rememberMeInput: false,
},
computed: {
- currentView: function() {
- // 4 - Login
- if (this.loggedIn == false) {
- return 4;
- }
- // 3 - Edit Note
- else if (this.currentNote && this.editMode) {
- return 3;
- }
- // 2 - View Note
- else if (this.currentNote) {
- return 2;
- }
- // 1 - Search Results
- else if (this.searchResults) {
- return 1;
- }
- // 0 - Notes List
- else {
- return 0;
- }
- },
-
notesByLastModifiedDesc: function() {
return this.notes.sort(function(a, b) {
return b.lastModified - a.lastModified;
},
},
- watch: {
- searchTerm: function() {
- this.clearSearchTimeout();
- if (this.searchTerm) {
- this.startSearchTimeout();
- } else {
- this.searchResults = null;
+ methods: {
+ route: function() {
+ let path = window.location.pathname.split("/");
+ let basePath = path[1];
+
+ // Home Page
+ if (basePath == "") {
+ this.getNotes();
+ this.currentView = this.views.home;
+ }
+
+ // Search
+ else if (basePath == constants.basePaths.search) {
+ this.searchTerm = helpers.getSearchParam(constants.params.searchTerm);
+ this.getSearchResults();
+ this.currentView = this.views.search;
+ }
+
+ // Note
+ else if (basePath == constants.basePaths.note) {
+ let noteTitle = path[2];
+ this.loadNote(noteTitle);
+ this.currentView = this.views.note;
+ }
+
+ // Login
+ else if (basePath == constants.basePaths.login) {
+ this.currentView = this.views.login;
}
+
+ this.updateDocumentTitle();
+ },
+
+ updateDocumentTitle: function() {
+ let pageTitleSuffix = null;
+ if (this.currentView == this.views.login) {
+ pageTitleSuffix = "Login";
+ } else if (this.currentView == this.views.search) {
+ pageTitleSuffix = "Search";
+ } else if (
+ this.currentView == this.views.note &&
+ this.currentNote != null
+ ) {
+ pageTitleSuffix = this.currentNote.title;
+ }
+ window.document.title =
+ (pageTitleSuffix ? `${pageTitleSuffix} - ` : "") + "flatnotes";
},
- },
- methods: {
login: function() {
let parent = this;
api
if (parent.rememberMeInput == true) {
localStorage.setItem("token", response.data.access_token);
}
- parent.loggedIn = true;
- parent.getNotes();
+ let redirectPath = helpers.getSearchParam(constants.params.redirect);
+ window.open(redirectPath || "/", "_self");
})
.finally(function() {
parent.usernameInput = null;
logout: function() {
sessionStorage.removeItem("token");
localStorage.removeItem("token");
- this.loggedIn = false;
+ window.open(`/${constants.basePaths.login}`, "_self");
},
getNotes: function() {
});
},
- clearSearchTimeout: function() {
- if (this.searchTimeout != null) {
- clearTimeout(this.searchTimeout);
- }
- },
-
- startSearchTimeout: function() {
- this.clearSearchTimeout();
- this.searchTimeout = setTimeout(this.search, 1000);
+ search: function() {
+ window.open(
+ `/${constants.basePaths.search}?${
+ constants.params.searchTerm
+ }=${encodeURI(this.searchTerm)}`,
+ "_self"
+ );
},
- search: function() {
- let parent = this;
- this.clearSearchTimeout();
- if (this.searchTerm) {
- api
- .get("/api/search", { params: { term: this.searchTerm } })
- .then(function(response) {
- parent.searchResults = [];
- response.data.forEach(function(result) {
- parent.searchResults.push(
- new SearchResult(
- result.filename,
- result.lastModified,
- result.titleHighlights,
- result.contentHighlights
- )
- );
- });
+ getSearchResults: function() {
+ var parent = this;
+ api
+ .get("/api/search", { params: { term: this.searchTerm } })
+ .then(function(response) {
+ parent.searchResults = [];
+ response.data.forEach(function(result) {
+ parent.searchResults.push(
+ new SearchResult(
+ result.filename,
+ result.lastModified,
+ result.titleHighlights,
+ result.contentHighlights
+ )
+ );
});
- }
+ });
},
loadNote: function(filename) {
let parent = this;
- api.get(`/api/notes/${filename}`).then(function(response) {
- parent.currentNote = response.data;
- parent.newFilename = parent.currentNote.filename;
- });
+ api
+ .get(`/api/notes/${filename}.${constants.markdownExt}`)
+ .then(function(response) {
+ parent.currentNote = new Note(
+ response.data.filename,
+ response.data.lastModified,
+ response.data.content
+ );
+ parent.newFilename = parent.currentNote.filename;
+ parent.updateDocumentTitle();
+ });
+ },
+
+ toggleEditMode: function() {
+ this.editMode = !this.editMode;
},
newNote: function() {
this.currentNote = new Note();
this.editMode = true;
- },
-
- unloadNote: function() {
- this.currentNote = null;
- this.newFilename = null;
- this.editMode = false;
- this.getNotes();
+ this.currentView = this.views.note;
},
saveNote: function() {
- let parent = this;
let newContent = this.$refs.toastUiEditor.invoke("getMarkdown");
// New Note
filename: this.newFilename,
content: newContent,
})
- .then(function(response) {
- parent.currentNote = response.data;
- parent.newFilename = parent.currentNote.filename;
- parent.editMode = false;
- });
+ .then(this.saveNoteResponseHandler);
}
// Modified Note
newFilename: this.newFilename,
newContent: newContent,
})
- .then(function(response) {
- parent.currentNote = response.data;
- parent.newFilename = parent.currentNote.filename;
- parent.editMode = false;
- });
+ .then(this.saveNoteResponseHandler);
}
// No Change
else {
- this.editMode = false;
+ this.toggleEditMode();
}
},
+
+ saveNoteResponseHandler: function(response) {
+ this.currentNote = new Note(
+ response.data.filename,
+ response.data.lastModified,
+ response.data.content
+ );
+ this.newFilename = this.currentNote.filename;
+ this.updateDocumentTitle();
+ history.replaceState(null, "", this.currentNote.href);
+ this.toggleEditMode();
+ },
},
created: function() {
let token = localStorage.getItem("token");
if (token != null) {
sessionStorage.setItem("token", token);
- this.loggedIn = true;
- this.getNotes();
}
+
+ this.route();
},
};
<div>
<!-- Header -->
<div class="mt-4 mb-4">
- <h1 class="text-center">flatnotes</h1>
+ <h1 class="h1 clickable-link text-center"><a href="/">flatnotes</a></h1>
+ </div>
+
+ <!-- Login -->
+ <div
+ v-if="currentView == views.login"
+ class="d-flex justify-content-center"
+ >
+ <form v-on:submit.prevent="login">
+ <div class="mb-3">
+ <label for="username" class="form-label">Username</label>
+ <input
+ type="text"
+ class="form-control"
+ id="username"
+ autocomplete="username"
+ v-model="usernameInput"
+ />
+ </div>
+ <div class="mb-3">
+ <label for="password" class="form-label">Password</label>
+ <input
+ type="password"
+ class="form-control"
+ id="password"
+ autocomplete="current-password"
+ v-model="passwordInput"
+ />
+ </div>
+ <div class="mb-3 form-check">
+ <input
+ type="checkbox"
+ class="form-check-input"
+ id="rememberMe"
+ v-model="rememberMeInput"
+ />
+ <label class="form-check-label" for="rememberMe">Remember Me</label>
+ </div>
+ <button type="submit" class="btn btn-primary">Log In</button>
+ </form>
</div>
<!-- Buttons -->
<div v-if="currentView != 4" class="d-flex justify-content-center mb-4">
<!-- Logout -->
<button
- v-if="currentView == 0"
+ v-if="currentView == views.home"
type="button"
class="btn btn-light mx-1"
@click="logout"
<!-- New -->
<button
- v-if="currentView == 0"
+ v-if="currentView == views.home"
type="button"
class="btn btn-primary mx-1"
@click="newNote"
</button>
<!-- Close -->
- <button
- v-if="currentView == 2"
- type="button"
- class="btn btn-secondary mx-1"
- @click="unloadNote"
- >
- Close
- </button>
+ <a href="/">
+ <button
+ v-if="currentView == 2 && editMode == false"
+ type="button"
+ class="btn btn-secondary mx-1"
+ >
+ Close
+ </button>
+ </a>
<!-- Edit -->
<button
- v-if="currentView == 2"
+ v-if="currentView == views.note && editMode == false"
type="button"
class="btn btn-warning mx-1"
- @click="editMode = true"
+ @click="toggleEditMode"
>
Edit
</button>
<!-- Cancel -->
<button
- v-if="currentView == 3"
+ v-if="currentView == views.note && editMode == true"
type="button"
class="btn btn-secondary mx-1"
- @click="editMode = false"
+ @click="toggleEditMode"
>
Cancel
</button>
<!-- Save -->
<button
- v-if="currentView == 3"
+ v-if="currentView == views.note && editMode == true"
type="button"
class="btn btn-success mx-1"
@click="saveNote"
</button>
</div>
- <!-- Login -->
- <div v-if="currentView == 4" class="d-flex justify-content-center">
- <form v-on:submit.prevent="login">
- <div class="mb-3">
- <label for="username" class="form-label">Username</label>
- <input
- type="text"
- class="form-control"
- id="username"
- autocomplete="username"
- v-model="usernameInput"
- />
- </div>
- <div class="mb-3">
- <label for="password" class="form-label">Password</label>
- <input
- type="password"
- class="form-control"
- id="password"
- autocomplete="current-password"
- v-model="passwordInput"
- />
- </div>
- <div class="mb-3 form-check">
- <input
- type="checkbox"
- class="form-check-input"
- id="rememberMe"
- v-model="rememberMeInput"
- />
- <label class="form-check-label" for="rememberMe">Remember Me</label>
- </div>
- <button type="submit" class="btn btn-primary">Log In</button>
- </form>
- </div>
+ <!-- Search Input -->
+ <form
+ v-if="[views.search, views.home].includes(currentView)"
+ v-on:submit.prevent="search"
+ >
+ <div class="form-group mb-4 d-flex justify-content-center">
+ <input
+ type="text"
+ class="form-control"
+ placeholder="Search"
+ v-model="searchTerm"
+ style="max-width: 500px"
+ />
+ </div>
+ </form>
- <!-- Viewer -->
- <div v-else-if="currentView == 2">
- <viewer :initialValue="currentNote.content" height="600px" />
- </div>
+ <!-- Note -->
+ <div v-if="currentView == views.note && currentNote != null">
+ <!-- Viewer -->
+ <div v-if="editMode == false">
+ <viewer :initialValue="currentNote.content" height="600px" />
+ </div>
- <!-- Editor -->
- <div v-else-if="currentView == 3">
- <input type="text" class="form-control" v-model="newFilename" />
- <editor
- :initialValue="currentNote.content"
- previewStyle="tab"
- height="calc(100vh - 180px)"
- ref="toastUiEditor"
- />
+ <!-- Editor -->
+ <div v-else>
+ <input type="text" class="form-control" v-model="newFilename" />
+ <editor
+ :initialValue="currentNote.content"
+ previewStyle="tab"
+ height="calc(100vh - 180px)"
+ ref="toastUiEditor"
+ />
+ </div>
</div>
- <!-- Front Page -->
- <div v-else>
- <!-- Search Input -->
- <form v-on:submit.prevent="search">
- <div class="form-group mb-4 d-flex justify-content-center">
- <input
- type="text"
- class="form-control"
- placeholder="Search"
- v-model="searchTerm"
- style="max-width: 500px"
- />
- </div>
- </form>
-
+ <!-- Search -->
+ <div v-if="currentView == views.search">
<!-- Search Results -->
- <div v-if="currentView == 1">
+ <div v-if="searchResults">
<div
v-for="result in searchResults"
:key="result.filename"
class="mb-5"
>
- <p
- class="h5 text-center clickable-link"
- v-html="result.titleHighlightsOrTitle"
- @click="loadNote(result.filename)"
- ></p>
+ <p class="h5 text-center clickable-link">
+ <a v-html="result.titleHighlightsOrTitle" :href="result.href"></a>
+ </p>
<p
class="text-center text-muted"
v-html="result.contentHighlights"
></p>
</div>
</div>
+ </div>
- <!-- Notes -->
- <div v-else>
- <p
- v-for="note in notesByLastModifiedDesc"
- :key="note.filename"
- class="text-center clickable-link mb-2"
- @click="loadNote(note.filename)"
- >
- {{ note.title }}
- </p>
- </div>
+ <!-- Home -->
+ <div v-if="currentView == views.home">
+ <p
+ v-for="note in notesByLastModifiedDesc"
+ class="text-center clickable-link mb-2"
+ :key="note.filename"
+ >
+ <a :href="note.href">{{ note.title }}</a>
+ </p>
</div>
</div>
</div>