fuzzy-searching, parallax
This commit is contained in:
parent
5a1467c971
commit
817057a861
5
package-lock.json
generated
5
package-lock.json
generated
@ -2736,6 +2736,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mongoose-fuzzy-searching": {
|
||||||
|
"version": "1.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongoose-fuzzy-searching/-/mongoose-fuzzy-searching-1.3.1.tgz",
|
||||||
|
"integrity": "sha512-Vi+EwmYPoxZzgwBOuTg5FBjXqbX1gjbXxvX/4Ypo7yC2aGKwKfgEeenAVwU6DPirL/TDj6jLGEoblzMJ3+HWCg=="
|
||||||
|
},
|
||||||
"mongoose-legacy-pluralize": {
|
"mongoose-legacy-pluralize": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz",
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-session": "^1.17.1",
|
"express-session": "^1.17.1",
|
||||||
"mongoose": "^5.9.18",
|
"mongoose": "^5.9.18",
|
||||||
|
"mongoose-fuzzy-searching": "^1.3.1",
|
||||||
"multer": "^1.4.2",
|
"multer": "^1.4.2",
|
||||||
"passport": "^0.4.1",
|
"passport": "^0.4.1",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
|
@ -1,6 +1,15 @@
|
|||||||
<script>
|
<script>
|
||||||
import { stores } from '@sapper/app';
|
import { stores, goto } from '@sapper/app';
|
||||||
const { session } = stores();
|
const { session } = stores();
|
||||||
|
|
||||||
|
let query = '';
|
||||||
|
|
||||||
|
function search(e)
|
||||||
|
{
|
||||||
|
if (e.keyCode === 13) {
|
||||||
|
goto(`/search/${encodeURIComponent(query)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@ -23,11 +32,10 @@
|
|||||||
padding: 0 1.5rem 0.25rem;
|
padding: 0 1.5rem 0.25rem;
|
||||||
align-items: start;
|
align-items: start;
|
||||||
}
|
}
|
||||||
div.filler {
|
div.search {
|
||||||
flex: 1 0 0;
|
flex: 1 0 0;
|
||||||
}
|
margin: auto 0;
|
||||||
div.items div {
|
display: flex;
|
||||||
display: block;
|
|
||||||
}
|
}
|
||||||
div.link a {
|
div.link a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@ -48,12 +56,21 @@
|
|||||||
margin-left: 4rem;
|
margin-left: 4rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
input.search {
|
||||||
|
height: 1.5rem;
|
||||||
|
width: 50%;
|
||||||
|
font-size: 1rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
<div class="items">
|
<div class="items">
|
||||||
<div><a href="/"><img class="wordmark" src="/logo.png" alt="HowFeed.biz"></a></div>
|
<div><a href="/"><img class="wordmark" src="/logo.png" alt="HowFeed.biz"></a></div>
|
||||||
<div class="filler"></div>
|
<div class="search">
|
||||||
|
<input class="search" on:keyup={search} bind:value={query} type="text" placeholder="Search">
|
||||||
|
</div>
|
||||||
{#if !$session.user}
|
{#if !$session.user}
|
||||||
<div class="link"><a href="/contact">Contact Us</a></div>
|
<div class="link"><a href="/contact">Contact Us</a></div>
|
||||||
{:else}
|
{:else}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
|
import mongoose_fuzzy_searching from 'mongoose-fuzzy-searching';
|
||||||
|
|
||||||
const { Schema } = mongoose;
|
const { Schema } = mongoose;
|
||||||
const ArticleSchema = new Schema({
|
const ArticleSchema = new Schema({
|
||||||
@ -28,13 +29,18 @@ const ArticleSchema = new Schema({
|
|||||||
|
|
||||||
ArticleSchema.methods.genSlug = title => title.toLowerCase().replace(/\W+/g, '-');
|
ArticleSchema.methods.genSlug = title => title.toLowerCase().replace(/\W+/g, '-');
|
||||||
|
|
||||||
ArticleSchema.pre('save', function (next) {
|
ArticleSchema.plugin(mongoose_fuzzy_searching, {
|
||||||
var article = this;
|
fields: ['html', 'title'],
|
||||||
// only gen the slug if title has been modified (or is new)
|
middlewares: {
|
||||||
if (!article.isModified('title')) return next();
|
preSave: function () {
|
||||||
|
var article = this;
|
||||||
|
// only gen the slug if title has been modified (or is new)
|
||||||
|
if (!article.isModified('title'))
|
||||||
|
return;
|
||||||
|
|
||||||
article.slug = article.genSlug(article.title);
|
article.slug = article.genSlug(article.title);
|
||||||
next();
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default mongoose.model('Article', ArticleSchema);
|
export default mongoose.model('Article', ArticleSchema);
|
||||||
|
@ -83,7 +83,7 @@
|
|||||||
async function addCategory()
|
async function addCategory()
|
||||||
{
|
{
|
||||||
let name = prompt('Enter a category name');
|
let name = prompt('Enter a category name');
|
||||||
if (name) {
|
if (name !== null) {
|
||||||
const res = await fetch('/cms/category', {
|
const res = await fetch('/cms/category', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@ -92,8 +92,13 @@
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({ name })
|
body: JSON.stringify({ name })
|
||||||
});
|
});
|
||||||
categories = await res.json();
|
const json = await res.json();
|
||||||
category = categories.filter(c => c.name === name)[0].slug;
|
if (res.status === 200) {
|
||||||
|
categories = json;
|
||||||
|
category = categories.filter(c => c.name === name)[0].slug;
|
||||||
|
} else {
|
||||||
|
alert(`Error ${res.status}: ${json.message}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
<h3>James N. Shiffer</h3>
|
<h3>James N. Shiffer</h3>
|
||||||
<p>Webmaster</p>
|
<p>Webmaster</p>
|
||||||
<p>FemboyFinancial Holdings Co., Ltd. (USA LLC)</p>
|
<p>FemboyFinancial Holdings Co., Ltd. (USA LLC)</p>
|
||||||
<p><a href="mailto:webmaster@femboyfashion.com.hk">webmaster@femboyfashion.com.hk</a></p>
|
<p><a href="mailto:webmaster@howfeed.biz">webmaster@howfeed.biz</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="max-width:100%;margin:0 auto;width:700px;height:440px;">
|
<div style="max-width:100%;margin:0 auto;width:700px;height:440px;">
|
||||||
|
55
src/routes/search/[query].svelte
Normal file
55
src/routes/search/[query].svelte
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<script context="module">
|
||||||
|
export async function preload({ params })
|
||||||
|
{
|
||||||
|
let query = params.query;
|
||||||
|
const res = await this.fetch(`/search.json?query=${encodeURIComponent(query)}`);
|
||||||
|
const json = await res.json();
|
||||||
|
if (res.status === 200) {
|
||||||
|
return { query, results: json };
|
||||||
|
} else {
|
||||||
|
this.error(res.status, res.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export let query;
|
||||||
|
export let results;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h1 {
|
||||||
|
margin: 0 auto;
|
||||||
|
color: whitesmoke;
|
||||||
|
margin-top: 1rem;
|
||||||
|
font-size: 2rem;
|
||||||
|
font-size: 3rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
text-align: center;
|
||||||
|
max-width: 80%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Search Results for: {query} | HOWFEED.BIZ</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="background"></div>
|
||||||
|
<div class="floaty">
|
||||||
|
<h1>Search Results for: {query}</h1>
|
||||||
|
<div class="article-list">
|
||||||
|
{#each results as {title, slug, image, created_at}}
|
||||||
|
<a rel="prefetch" href={`/a/${slug}`}>
|
||||||
|
<figure class="article-image">
|
||||||
|
<img src={image || '/logo.png'} alt={title}>
|
||||||
|
</figure>
|
||||||
|
<div class="article-meta">
|
||||||
|
<p class="article-title">{title}</p>
|
||||||
|
<p class="article-date">{new Date(created_at).toLocaleDateString()}</p>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
{:else}
|
||||||
|
<p>No results found :(</p>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
20
src/routes/search/index.json.js
Normal file
20
src/routes/search/index.json.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import Article from '../../models/article.js';
|
||||||
|
|
||||||
|
export async function get(req, res)
|
||||||
|
{
|
||||||
|
const { query } = req.query;
|
||||||
|
if (!query) {
|
||||||
|
res.writeHead(422, {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
});
|
||||||
|
res.end(JSON.stringify({
|
||||||
|
message: `No search query provided`
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const results = await Article.fuzzySearch(query);
|
||||||
|
res.writeHead(200, {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
});
|
||||||
|
res.end(JSON.stringify(results));
|
||||||
|
}
|
18
src/routes/search/index.svelte
Normal file
18
src/routes/search/index.svelte
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<script>
|
||||||
|
import { goto } from '@sapper/app';
|
||||||
|
|
||||||
|
let query = '';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
input {
|
||||||
|
width: 440px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<h1>Search Results</h1>
|
||||||
|
<p>You haven't entered any search terms.</p>
|
||||||
|
<input type="text" placeholder="Search Articles" bind:value={query}>
|
||||||
|
<button type="submit" on:click={() => { goto(`/search/${encodeURIComponent(query)}`); }}>Search</button>
|
||||||
|
</div>
|
@ -115,6 +115,7 @@ div.article-meta {
|
|||||||
div.background {
|
div.background {
|
||||||
background: url('/cityscape.jpg') no-repeat center;
|
background: url('/cityscape.jpg') no-repeat center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
background-attachment: fixed;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
height: 24rem;
|
height: 24rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user