There's nothing here 😢
Why don't you start following somebody? 👻
+
+
+
This is the end of your stream. Hooray! 👻
+
+
+
+
+
diff --git a/webui/src/views/LoginView.vue b/webui/src/views/LoginView.vue
index 103b07e..1eef8ee 100644
--- a/webui/src/views/LoginView.vue
+++ b/webui/src/views/LoginView.vue
@@ -2,18 +2,26 @@
export default {
data: function () {
return {
+ // The error message to display
errormsg: null,
+
+ // Loading spinner state
loading: false,
- some_data: null,
+
+ // Form inputs
field_username: "",
rememberLogin: false,
};
},
methods: {
+ // Send the login request to the server
+ // if the login is successful, the token is saved
+ // and the user is redirected to the previous page
async login() {
this.loading = true;
this.errormsg = null;
+ // Send the login request
let response = await this.$axios.post("/session", {
name: this.field_username,
});
@@ -24,6 +32,7 @@ export default {
return
}
+ // If the login is successful, save the token and redirect to the previous page
if (response.status == 201 || response.status == 200) {
// Save the token in the local storage if the user wants to be remembered
if (this.rememberLogin) {
@@ -44,9 +53,10 @@ export default {
this.$router.go(-1);
}
else {
+ // Login failed, show the error message
this.errormsg = response.data["error"];
}
-
+ // Disable the loading spinner
this.loading = false;
},
},
@@ -54,6 +64,7 @@ export default {
+
diff --git a/webui/src/views/ProfileView.vue b/webui/src/views/ProfileView.vue
index b362f3b..78093e4 100644
--- a/webui/src/views/ProfileView.vue
+++ b/webui/src/views/ProfileView.vue
@@ -4,64 +4,95 @@ import IntersectionObserver from '../components/IntersectionObserver.vue';
export default {
data: function () {
return {
+ // The profile to show
requestedProfile: this.$route.params.user_id,
+
+ // Loading flags
loading: true,
loadingError: false,
+
+ // Profile data from the server
+ user_data: [],
+
+ // Protos data from the server
stream_data: [],
+
+ // Dynamic loading parameters
data_ended: false,
start_idx: 0,
limit: 1,
- user_data: [],
};
},
methods: {
async refresh() {
+ // Fetch profile info from the server
this.getMainData();
- // this way we are sure that we fill the first page todo: check
- // 450 is a bit more of the max height of a post
- // todo: may not work in 4k screens :/
+
+ // Limits the number of posts to load based on the window height
+ // to avoid loading too many posts at once
+ // 450px is (a bit more) of the height of a single post
this.limit = Math.max(Math.round(window.innerHeight / 450), 1);
+
+ // Reset the parameters and the data
this.start_idx = 0;
this.data_ended = false;
this.stream_data = [];
+
+ // Fetch the first batch of posts
this.loadContent();
},
+
+ // Fetch profile info from the server
async getMainData() {
let response = await this.$axios.get("/users/" + this.requestedProfile);
if (response == null) {
+ // An error occurred, set the error flag
this.loading = false;
this.loadingError = true;
return;
}
this.user_data = response.data;
},
+
+ // Fetch photos from the server
async loadContent() {
this.loading = true;
let response = await this.$axios.get("/users/" + this.requestedProfile + "/photos" + "?start_index=" + this.start_idx + "&limit=" + this.limit);
- if (response == null) {
- // do something
- return;
- }
+ if (response == null) return // An error occurred. The interceptor will show a modal
+
+ // If the server returned less elements than requested,
+ // it means that there are no more photos to load
if (response.data.length == 0 || response.data.length < this.limit)
- this.data_ended = true;
- this.stream_data = this.stream_data.concat(response.data);
- this.loading = false;
+ this.data_ended = true
+
+ // Append the new photos to the array
+ this.stream_data = this.stream_data.concat(response.data)
+
+ // Disable the loading spinner
+ this.loading = false
},
+
+ // Load more photos when the user scrolls to the bottom of the page
loadMore() {
+ // Avoid sending a request if there are no more photos
if (this.loading || this.data_ended) return
+
+ // Increase the start index and load more photos
this.start_idx += this.limit
this.loadContent()
},
},
created() {
if (this.$route.params.user_id == "me") {
- //this.$router.replace({ path: "/profile/" + }); (It's ok to not redirect, it's just a matter of taste)
+ // If the id is "me", show the current user's profile
this.requestedProfile = this.$currentSession();
}
else {
+ // Otherwise, show "id"'s profile
this.requestedProfile = this.$route.params.user_id;
}
- //this.scroll();
+
+ // Fetch the profile info and the first batch of photos
this.refresh();
},
components: { IntersectionObserver }
@@ -75,10 +106,12 @@ export default {
+
+
{{ user_data["photos"] }}
@@ -94,22 +127,30 @@ export default {
+
+
+
You reached the end. Hooray! 👻
+
+
+
+
+
diff --git a/webui/src/views/SearchView.vue b/webui/src/views/SearchView.vue
index 92a9c8b..baa1811 100644
--- a/webui/src/views/SearchView.vue
+++ b/webui/src/views/SearchView.vue
@@ -3,32 +3,52 @@
export default {
data: function () {
return {
+ // The error message to display
errormsg: null,
+
loading: false,
+
+ // Search results
streamData: [],
+
+ // Dynamic loading
dataEnded: false,
startIdx: 0,
limit: 1,
+
+ // Search input
fieldUsername: "",
}
},
methods: {
- async refresh() {
+ // Reset the results and fetch the new requested ones
+ async query() {
+ // Set the limit to the number of cards that can fit in the window
this.limit = Math.round(window.innerHeight / 72);
+
+ // Reset the parameters and the data
this.startIdx = 0;
this.dataEnded = false;
this.streamData = [];
+
+ // Fetch the first batch of results
this.loadContent();
},
+
+ // Fetch the search results from the server
async loadContent() {
this.loading = true;
this.errormsg = null;
+
+ // Check if the username is empty
+ // and show an error message
if (this.fieldUsername == "") {
this.errormsg = "Please enter a username";
this.loading = false;
return;
}
+ // Fetch the results from the server
let response = await this.$axios.get("/users?query=" + this.fieldUsername + "&start_index=" + this.startIdx + "&limit=" + this.limit);
// Errors are handled by the interceptor, which shows a modal dialog to the user and returns a null response.
@@ -37,23 +57,23 @@ export default {
return
}
+ // If there are no more results, set the dataEnded flag
if (response.data.length == 0) this.dataEnded = true;
- else this.streamData = this.streamData.concat(response.data);
- this.loading = false;
+ // Otherwise, append the new results to the array
+ else this.streamData = this.streamData.concat(response.data);
+
+ // Hide the loading spinner
+ this.loading = false;
},
+
+ // Load a new batch of results when the user scrolls to the bottom of the page
loadMore() {
if (this.loading || this.dataEnded) return
this.startIdx += this.limit
this.loadContent()
},
},
- mounted() {
- // this way we are sure that we fill the first page
- // 72 is a bit more of the max height of a card
- // todo: may not work in 4k screens :/
- this.limit = Math.round(window.innerHeight / 72)
- }
}
@@ -65,20 +85,27 @@ export default {