diff --git a/css/car-pictures.css b/css/car-pictures.css
index 3c69bc1..fe57781 100644
--- a/css/car-pictures.css
+++ b/css/car-pictures.css
@@ -18,9 +18,9 @@ section {
}
.approval-btn {
- margin-top: 29px;
+ /* margin-top: 29px; */
align-self: flex-start;
- margin-left: 15px;
+ /* margin-left: 15px; */
}
#button-and-posts-separating-line {
@@ -29,13 +29,48 @@ section {
color: azure;
}
+#aboveTopLineDiv {
+ display: flex;
+ width: 95%;
+ align-items: center;
+ justify-content: space-evenly;
+ margin-top: 16px;
+}
+
+#aboveTopLineDiv > form {
+ width: 50%;
+ margin: 0 !important;
+}
+
+#aboveTopLineDiv > form > input.form-control{
+ width: 50%;
+ outline: none !important;
+ box-shadow: unset !important;
+}
+
+#aboveTopLineDiv > form > .btn {
+ border-radius: 0px !important;
+}
+
+#aboveTopLineDiv > form > .btn:last-child {
+ border-top-right-radius: 3px !important;
+ border-bottom-right-radius: 3px !important;
+}
+
+/* TODO: Back4app remove post from Redis */
+
+#aboveTopLineDiv > form > .btn-secondary{
+ border-top-left-radius: 0px !important;
+ border-bottom-left-radius: 0px !important;
+}
+
section {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
align-items: center;
- margin-top: 55px;
+ margin-top: 66px;
padding-bottom: 2%;
height: auto;
min-height: 100vh;
@@ -46,11 +81,17 @@ section {
--card-margin-top: 30px;
}
+@media only screen and (min-width: 1440px) {
+ section {
+ margin-top: calc(4.4vw) !important;
+ }
+}
+
#unapprovedPostsMessageContainer {
width: var(--card-width);
min-width: 250px;
- margin-top: 34px;
+ /* margin-top: 34px; */
padding: 13px;
border: 5px dashed darkkhaki;
@@ -75,7 +116,7 @@ section {
}
.card {
- margin-top: var(--card-margin-top) !important;
+ margin-top: 14px !important;
width: var(--card-width) !important;
min-width: 250px !important;
border: none !important;
@@ -1098,6 +1139,7 @@ ul.extra-comment-actions:focus-within {
user-select: none;
pointer-events: none;
justify-self: center;
+ text-align: center;
}
.loader {
@@ -1166,6 +1208,14 @@ ul.extra-comment-actions:focus-within {
}
}
+#loader-wrapper {
+ width: 100%;
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
/* ? Custom scrollbar */
/* Firefox */
* {
diff --git a/src/api/posts.js b/src/api/posts.js
index 166cf7e..00398c6 100644
--- a/src/api/posts.js
+++ b/src/api/posts.js
@@ -9,7 +9,7 @@ export async function* get2PostObjects() {
if (result.length == 0) {
//? If we didn't get any posts from server, return an empty array and signal the end of this generator.
- return [];
+ return [];
} else {
yield result; //? Otherwise, yield the result and wait for the next call.
}
@@ -31,6 +31,38 @@ export async function* get2UnapprovedPostObjects() {
}
}
+export async function* get2SearchedPostObjects(searchedPostsIds) {
+ for (let i = 0; i < searchedPostsIds.length; i += 2) {
+ const result = (await api.post('/functions/get2PostsByIds', { searchedPostsIds: JSON.stringify(searchedPostsIds.slice(i, i + 2)) })).result;
+
+ if (result.length == 0) {
+ //? If we didn't get any posts from server, return an empty array and signal the end of this generator.
+ return [];
+ } else if (result.length == 1) {
+ //? if we got the last post, signal the end of this generator.
+ return result;
+ } else {
+ yield result; //? Otherwise, yield the result and wait for the next call.
+ }
+ }
+}
+
+export async function* get2UnapprovedSearchedPostObjects(searchedPostsIds) {
+ for (let i = 0; i < searchedPostsIds.length; i += 2) {
+ const result = (await api.post('/functions/get2UnapprovedPostsByIds', { searchedPostsIds: JSON.stringify(searchedPostsIds.slice(i, i + 2)) })).result;
+
+ if (result.length == 0) {
+ //? If we didn't get any posts from server, return an empty array and signal the end of this generator.
+ return [];
+ } else if (result.length == 1) {
+ //? if we got the last post, signal the end of this generator.
+ return result;
+ } else {
+ yield result; //? Otherwise, yield the result and wait for the next call.
+ }
+ }
+}
+
async function getUnapprovedPostsCount() {
return (await api.post('/functions/getUnapprovedPostsCount')).result
}
@@ -74,8 +106,8 @@ export async function likePost(postId) {
}
export async function unlikePost(postId, userId) {
- const { objectId: likeObjId } =
- (await api.get(`/PostsLikes?where={"postId": "${postId}",
+ const { objectId: likeObjId } =
+ (await api.get(`/PostsLikes?where={"postId": "${postId}",
"userWhoLiked": {"__type":"Pointer","className":"_User","objectId":"${userId}"}}`)).results[0];
return api.del(`/PostsLikes/${likeObjId}`);
}
@@ -83,4 +115,28 @@ export async function unlikePost(postId, userId) {
export async function reportObject(report) {
addEntryWithUserPointer(report, 'reporter');
await api.post('/functions/reportObject', report);
+}
+
+export async function searchForPosts(srchText) {
+ try {
+ const response = await fetch("https://ex0tic-cars.netlify.app/.netlify/functions/search",
+ { body: srchText, method: 'POST' });
+
+ if (!response.ok) {
+ const error = await response.json();
+ const err = new Error(error.error);
+ err.code = error.code;
+ throw err;
+ }
+
+ if (response.status == 204) {
+ return [];
+ } else {
+ const srchResultsArr = await response.json();
+ return srchResultsArr;
+ }
+ } catch (error) {
+ alert(error.message);
+ throw error;
+ }
}
\ No newline at end of file
diff --git a/src/app.js b/src/app.js
index 66c8f80..2edb532 100644
--- a/src/app.js
+++ b/src/app.js
@@ -13,6 +13,13 @@ import { sharePhotosView } from "./views/share-photos.js";
import { showroomsView } from "./views/showrooms.js";
import { verifyView } from "./views/verify.js";
+sessionStorage.removeItem('popstateChanges');
+
+window.addEventListener('popstate', () => {
+ let popstateChanges = Number(sessionStorage.getItem('popstateChanges') || 0);
+ sessionStorage.setItem('popstateChanges', ++popstateChanges);
+});
+
const main = document.querySelector('#main');
const root = main.attachShadow({ mode: 'open' });
@@ -32,14 +39,18 @@ page((ctx, next) => {
page('/index.html', '/');
page('/', homeView);
page('/car-pictures', carPicturesView);
+page.exit('/car-pictures', (ctx, next) => {
+ ctx.carPicturesController?.abort();
+ next();
+});
page('/about-us', aboutUsView);
page('/showrooms', showroomsView);
page('/share-photos', sharePhotosView);
page('/share-photos:id', editPostView);
-page.exit('/share-photos', exitFunction);
-page.exit('/share-photos:id', exitFunction);
-function exitFunction(ctx, next) {
+page.exit('/share-photos', sharePhotosExitFunction);
+page.exit('/share-photos:id', sharePhotosExitFunction);
+function sharePhotosExitFunction(ctx, next) {
ctx.controller.abort(); //? remove navbar's listener for click event after user leaves "share-photos"
sessionStorage.removeItem('userHasUnsavedData');
next();
diff --git a/src/views/car-pictures/car-pictures.js b/src/views/car-pictures/car-pictures.js
index 81a0a83..ebbd9bb 100644
--- a/src/views/car-pictures/car-pictures.js
+++ b/src/views/car-pictures/car-pictures.js
@@ -1,89 +1,42 @@
-import { html } from "../../lib/lit-html.js";
-import { until } from "../../lib/directives/until.js";
-import { get2PostObjects, get2UnapprovedPostObjects } from "../../api/posts.js";
-import { sectionClickHandler } from "./infoWindow/infoWindow.js";
-import { carPicturesTemplate } from "./carPicturesTemplate.js";
-import { getSwitchToApprovalModeHandler } from "./posts/switchToApprovalModeHandler.js";
-import { getNoPostsTemplate } from "./posts/getNoPostsTemplate.js";
-import { getUnapprovedPostsMessageTemplate } from "./posts/unapprovedPostsMessageTemplate.js";
-import { getSectionContentTemplate } from "./posts/sectionContentTemplate.js";
-import { setUpMiscStuff } from "./posts/setUpMiscStuff.js";
-import { startObservingTheThirdLastCard } from "./posts/startObservingTheThirdLastCard.js";
+import { renderNew, resetState } from "./renderNew.js";
export async function carPicturesView(ctx) {
- const loadingTemplate = () => html`
-
Loading posts
-
- `
- const posts = [];
- const generatorsObject = {
- asyncPostsGenerator: get2PostObjects(),
- asyncPostsGeneratorIsDone: false,
- asyncUnapprovedPostsGenerator: get2UnapprovedPostObjects(),
- asyncUnapprovedPostsGeneratorIsDone: false,
+ if (!ctx.user?.isModerator && ctx.hash == 'approval-mode') {
+ ctx.page.redirect('/car-pictures');
+ return;
}
- let miscStuffSetUp = false;
- let portionsRendered = 0;
-
- async function renderNew() {
- if (generatorsObject.asyncPostsGeneratorIsDone
- || generatorsObject.asyncUnapprovedPostsGeneratorIsDone) {
- //? If one of the generators is exhausted, don't bother rendering anything.
- return;
- }
- const sectionContentPromise = getSectionContentTemplate(
- getNoPostsTemplate,
- ctx.hash == 'approval-mode' ? 'unapproved' : null,
- posts,
- generatorsObject,
- ctx,
- );
+ const posts = [];
+ resetState(posts);
- if (posts.length > 0) {
- //? If we already have 2 posts, the displaying of the "loading" template is avoided and the posts are shown after they have loaded.
- await sectionContentPromise;
- }
+ ctx.carPicturesController = new AbortController();
+ window.addEventListener('popstate', () => {
+ resetState(posts, ctx.user?.isModerator);
- /*
- ? After awaiting the sectionContentPromise and resuming the execution of this async function,
- ? we check if the page's hash is different from the hash in the "ctx" object
- ? (the post mode has been switched to something else
- ? while the sectionContentPromise was still in "pending" state).
- ? If this is the case we won't bother rendering anything into the section.
- */
- if (window.location.hash.slice(1) !== ctx.hash) {
- return;
- }
+ const srchRegex = /(?<=search=)(?.*?)(?=&|$)/m;
- ctx.render(
- carPicturesTemplate(
- ev => sectionClickHandler(ev, posts, ctx),
- until(sectionContentPromise, loadingTemplate()),
- ctx.user?.isModerator ? getSwitchToApprovalModeHandler(ctx) : null,
- ctx.user ? until(getUnapprovedPostsMessageTemplate(sectionContentPromise), null) : null,
- ctx,
- )
- );
+ const queryStr = window.location.search.slice(1);
- if (!miscStuffSetUp) {
- setUpMiscStuff(ctx, sectionContentPromise);
- miscStuffSetUp = true;
+ let urlSrchText = null; //? this text will be used in renderNew for auto search when first rendering
+ if (queryStr) {
+ urlSrchText = srchRegex.exec(decodeURIComponent(queryStr)).groups.srchStr.trim();
}
- await sectionContentPromise; //? we wait for the two cards to show up on the screen
+ //? (in getSectionContentTemplate) the window path may change while some post promises are still pending so we create this variable to prevent the rendering of posts in a wrong view
+ let windowPath = window.location.href.replace(window.location.origin, '');
+ windowPath = window.location.hash ? windowPath : windowPath.replace("#", '');
+
+ renderNew(ctx, posts, urlSrchText ? [] : null, urlSrchText || null, windowPath);
+ }, { signal: ctx.carPicturesController.signal });
- portionsRendered++;
+ if (posts.length == 0) { //? If there are no posts, start rendering them.
+ const srchRegex = /(?<=search=)(?.*?)(?=&|$)/m;
- if (portionsRendered < 2) {
- renderNew(); //? render one more portion
- } else {
- portionsRendered = 0;
- startObservingTheThirdLastCard(ctx, renderNew); //? When user sees the third card, repeat the rendering cycle.
+ let urlSrchText = null; //? this text will be used in renderNew for auto search when first rendering
+ if (ctx.querystring) {
+ urlSrchText = srchRegex.exec(decodeURIComponent(ctx.querystring)).groups.srchStr.trim();
}
- }
- if (posts.length == 0) { //? If there are no posts, start rendering them.
- renderNew();
+ renderNew(ctx, posts, urlSrchText ? [] : null, urlSrchText || null);
}
}
\ No newline at end of file
diff --git a/src/views/car-pictures/carPicturesTemplate.js b/src/views/car-pictures/carPicturesTemplate.js
index c6e9386..b27e01a 100644
--- a/src/views/car-pictures/carPicturesTemplate.js
+++ b/src/views/car-pictures/carPicturesTemplate.js
@@ -1,6 +1,6 @@
import { html } from "../../lib/lit-html.js";
-export const carPicturesTemplate = (sectionClickHandler, postsTemplate, onSwitchToApprovalMode, unapprovedPostsMessageTemplatePromise, ctx) => html`
+export const carPicturesTemplate = (sectionClickHandler, postsTemplate, onSwitchToApprovalMode, unapprovedPostsMessageTemplatePromise, searchHandler, ctx, searchMode) => html`
@@ -8,13 +8,25 @@ export const carPicturesTemplate = (sectionClickHandler, postsTemplate, onSwitch
- ${
- onSwitchToApprovalMode ?
- html`
-
-
- ` : null
- }
+