Merge pull request #425 from Powersource/image-search

Image search
This commit is contained in:
Sean Billig 2020-05-07 08:44:44 -07:00 committed by GitHub
commit 58765f8b0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 148 additions and 19 deletions

47
package-lock.json generated
View File

@ -305,9 +305,9 @@
"integrity": "sha512-Lnle0J8t+Z+jFg78GFFnGo+Fphxaco9K9SppeBDsI27QBRBumxeGAMeOg5wt6XbAuj5pQWmAEWWEwPz4PYGMHw=="
},
"@fraction/flotilla": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@fraction/flotilla/-/flotilla-5.1.0.tgz",
"integrity": "sha512-0y/obrwkU7OtJAgQ1LWdaTZJS8tDXJfgq2wvPezqf9J7BvmnN1rYlTBkzeBIVQ4S9o08o0hUhSJurDm30UUW1A==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@fraction/flotilla/-/flotilla-5.2.0.tgz",
"integrity": "sha512-9uF6Zq/K/qpO54aWkpMK247YbNHok42NMchkBNN5TVIMK+tQKbNW3YR4xgUyUCmVExaO1L5Xk9vHcevg8Pa1NQ==",
"requires": {
"debug": "^4.1.1",
"lodash.shuffle": "^4.2.0",
@ -324,6 +324,7 @@
"ssb-lan": "^0.2.0",
"ssb-logging": "^1.0.0",
"ssb-master": "^1.0.3",
"ssb-meme": "^1.0.4",
"ssb-no-auth": "^1.0.0",
"ssb-onion": "^1.0.0",
"ssb-ooo": "^1.3.1",
@ -477,7 +478,7 @@
},
"@types/debug": {
"version": "4.1.5",
"resolved": "",
"resolved": false,
"integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==",
"dev": true
},
@ -529,7 +530,7 @@
},
"@types/highlight.js": {
"version": "9.12.3",
"resolved": "",
"resolved": false,
"integrity": "sha512-pGF/zvYOACZ/gLGWdQH8zSwteQS1epp68yRcVLJMgUck/MjEn/FBYmPub9pXT8C1e4a8YZfHo1CKyV8q1vKUnQ==",
"dev": true
},
@ -571,7 +572,7 @@
},
"@types/koa-mount": {
"version": "4.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-56iBULArwY3uKLl28eRFchZ2v0diEoJzJbDaHH/ehgruF/s2/KMHyWsKcIhvDJ3tGdKu9oZNQvxaMg++1IKFdA==",
"dev": true,
"requires": {
@ -589,7 +590,7 @@
},
"@types/koa-static": {
"version": "4.0.1",
"resolved": "",
"resolved": false,
"integrity": "sha512-SSpct5fEcAeRkBHa3RiwCIRfDHcD1cZRhwRF///ZfvRt8KhoqRrhK6wpDlYPk/vWHVFE9hPGqh68bhzsHkir4w==",
"dev": true,
"requires": {
@ -599,7 +600,7 @@
},
"@types/koa__router": {
"version": "8.0.2",
"resolved": "",
"resolved": false,
"integrity": "sha512-3ZWfVAEcErHrZA31fWUC2YyZyAgoG4eKtQPy2XwBzdSpQealxjL7GcEEtGY925qPPs1wurW59qDl0KuRB39rrw==",
"dev": true,
"requires": {
@ -620,7 +621,7 @@
},
"@types/markdown-it": {
"version": "0.0.9",
"resolved": "",
"resolved": false,
"integrity": "sha512-IFSepyZXbF4dgSvsk8EsgaQ/8Msv1I5eTL0BZ0X3iGO9jw6tCVtPG8HchIPm3wrkmGdqZOD42kE0zplVi1gYDA==",
"dev": true,
"requires": {
@ -647,7 +648,7 @@
},
"@types/mkdirp": {
"version": "1.0.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-ONFY9//bCEr3DWKON3iDv/Q8LXnhaYYaNDeFSN0AtO5o4sLf9F0pstJKKKjQhXE0kJEeHs8eR6SAsROhhc2Csw==",
"dev": true,
"requires": {
@ -661,7 +662,7 @@
},
"@types/nodemon": {
"version": "1.19.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-nf0PKjNv3wo0pyhlPkHjfpU2oUYMdOIsCXceiFG6kvhYxbTwn2ne9mz2iWvJ/4QtCrJUEPJLNXuyGleyTacdaw==",
"dev": true,
"requires": {
@ -688,7 +689,7 @@
},
"@types/pull-stream": {
"version": "3.6.0",
"resolved": "",
"resolved": false,
"integrity": "sha512-d7cobAytjsYaTluqFTJopLbdZRs1HBkU+N07/zCtsXANZ5s9riDazRkvxkAUfmRzCvXqYAsGgnJHgX2lInIOQg==",
"dev": true
},
@ -774,7 +775,7 @@
},
"@types/yargs": {
"version": "15.0.4",
"resolved": "",
"resolved": false,
"integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==",
"dev": true,
"requires": {
@ -1583,7 +1584,7 @@
},
"changelog-version": {
"version": "1.0.1",
"resolved": "",
"resolved": "https://registry.npmjs.org/changelog-version/-/changelog-version-1.0.1.tgz",
"integrity": "sha512-WSY6Zq483wM31n+IhBb2f6uLgSQHCrui3VVmX4wmaxcszueYU15qDBhKzG+ZaKhv7ptfswPfv8epampii7s5Zw==",
"dev": true
},
@ -1915,7 +1916,7 @@
},
"common-good": {
"version": "2.0.3",
"resolved": "",
"resolved": "https://registry.npmjs.org/common-good/-/common-good-2.0.3.tgz",
"integrity": "sha512-GFFvmcOOpzts0x+DTR9vIcXr3Pcl1oKM/mYO4P0GQRkwqtO2B+30P4oH/32lmdCwlBdnaET2ZNYnb69Dklfdwg==",
"dev": true,
"requires": {
@ -9225,6 +9226,16 @@
"resolved": "https://registry.npmjs.org/ssb-master/-/ssb-master-1.0.3.tgz",
"integrity": "sha512-N1Cxm9WscGD9VEZrWbF2amyQai2U2g9gtq57W5zTqbhlQTLUUvl84U9A6fg6GPkECnUXadulnTw+mMYVkLLHjQ=="
},
"ssb-meme": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/ssb-meme/-/ssb-meme-1.0.4.tgz",
"integrity": "sha512-r4JAfdCY7tnRzMQNETdWfcm54FOe6bJMbpanp+9dQA76daLu1Jg3EijQ4jNtnDtv1WDMq1XcDTQyEtzVICrKBg==",
"requires": {
"flumeview-search": "^1.0.3",
"is-my-json-valid": "^2.17.2",
"ssb-ref": "^2.11.1"
}
},
"ssb-mentions": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/ssb-mentions/-/ssb-mentions-0.5.2.tgz",
@ -9976,7 +9987,7 @@
},
"stylelint-config-recommended": {
"version": "3.0.0",
"resolved": "",
"resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-3.0.0.tgz",
"integrity": "sha512-F6yTRuc06xr1h5Qw/ykb2LuFynJ2IxkKfCMf+1xqPffkxh0S09Zc902XCffcsw/XMFq/OzQ1w54fLIDtmRNHnQ==",
"dev": true
},
@ -10147,7 +10158,7 @@
"dependencies": {
"@babel/helpers": {
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz",
"resolved": false,
"integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==",
"requires": {
"@babel/template": "^7.8.3",
@ -10157,7 +10168,7 @@
},
"@babel/runtime": {
"version": "7.8.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz",
"resolved": false,
"integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==",
"requires": {
"regenerator-runtime": "^0.13.4"

View File

@ -22,7 +22,7 @@
},
"dependencies": {
"@fraction/base16-css": "^1.1.0",
"@fraction/flotilla": "^5.1.0",
"@fraction/flotilla": "^5.2.0",
"@koa/router": "^8.0.0",
"debug": "^4.1.1",
"env-paths": "^2.2.0",

View File

@ -491,6 +491,26 @@ input[type="search"] {
margin: var(--size-0) 0;
}
.image-search-grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: var(--size-0);
}
.image-search-grid .image-result {
display: flex;
flex-direction: column;
align-items: center;
padding: var(--size--1) 0;
background: var(--bg);
border-radius: var(--common-radius);
}
.image-search-grid .image-result .result-text {
hyphens: auto;
text-align: center;
}
hr {
border: var(--size--4) solid var(--fg-alt);
}

View File

@ -161,6 +161,7 @@ const {
publishView,
subtopicView,
searchView,
imageSearchView,
setLanguage,
settingsView,
topicsView,
@ -309,6 +310,13 @@ router
ctx.body = await searchView({ messages, query });
})
.get("/imageSearch", async (ctx) => {
const { query } = ctx.query;
const blobs = query ? await blob.search({ query }) : {};
ctx.body = await imageSearchView({ blobs, query });
})
.get("/inbox", async (ctx) => {
const inbox = async () => {
const messages = await post.inbox();

View File

@ -171,6 +171,18 @@ module.exports = ({ cooler, isPublic }) => {
// This does not wait for the blob.
ssb.blobs.want(blobId);
},
search: async ({ query }) => {
debug("blob search: %s", query);
const ssb = await cooler.open();
return new Promise((resolve, reject) => {
ssb.meme.search(query, (err, blobs) => {
if (err) return reject(err);
return resolve(blobs);
});
});
},
};
models.friend = {

View File

@ -51,6 +51,7 @@ const i18n = {
", sorted by recency. Private posts are encrypted for your public key, and have a maximum of 7 recipients. Recipients cannot be added after the thread has started. Select the timestamp to view the full thread.",
],
search: "Search",
imageSearch: "Image Search",
settings: "Settings",
// post actions
comment: "Comment",
@ -157,6 +158,8 @@ const i18n = {
acceptInvite: "Accept invite",
// search page
searchLabel: "Add word(s) to look for in downloaded messages.",
// image search page
imageSearchLabel: "Enter words to search for images labelled with them",
// posts and comments
commentDescription: ({ parentUrl }) => [
" commented on ",

View File

@ -132,6 +132,11 @@ const template = (...elements) => {
navLink({ href: "/mentions", emoji: "💬", text: i18n.mentions }),
navLink({ href: "/inbox", emoji: "✉️", text: i18n.private }),
navLink({ href: "/search", emoji: "🔍", text: i18n.search }),
navLink({
href: "/imageSearch",
emoji: "🖼️",
text: i18n.imageSearch,
}),
navLink({ href: "/settings", emoji: "⚙", text: i18n.settings })
)
),
@ -1041,6 +1046,76 @@ exports.searchView = ({ messages, query }) => {
);
};
const imageResult = ({ id, infos }) => {
const encodedBlobId = encodeURIComponent(id);
// only rendering the first message result so far
// todo: render links to the others as well
const info = infos[0];
const encodedMsgId = encodeURIComponent(info.msg);
return div(
{
class: "image-result",
},
[
a(
{
href: `/blob/${encodedBlobId}`,
},
img({ src: `/image/256/${encodedBlobId}` })
),
a(
{
href: `/thread/${encodedMsgId}#${encodedMsgId}`,
class: "result-text",
},
info.name
),
]
);
};
exports.imageSearchView = ({ blobs, query }) => {
const searchInput = input({
name: "query",
required: false,
type: "search",
value: query,
});
// - Minimum length of 3 because otherwise SSB-Search hangs forever. :)
// https://github.com/ssbc/ssb-search/issues/8
// - Using `setAttribute()` because HyperScript (the HyperAxe dependency has
// a bug where the `minlength` property is being ignored. No idea why.
// https://github.com/hyperhype/hyperscript/issues/91
searchInput.setAttribute("minlength", 3);
return template(
section(
h1(i18n.imageSearch),
form(
{ action: "/imageSearch", method: "get" },
label(i18n.imageSearchLabel, searchInput),
button(
{
type: "submit",
},
i18n.submit
)
)
),
div(
{
class: "image-search-grid",
},
Object.keys(blobs)
// todo: add pagination
.slice(0, 30)
.map((blobId) => imageResult({ id: blobId, infos: blobs[blobId] }))
)
);
};
exports.hashtagView = ({ messages, hashtag }) => {
return template(
section(h1(`#${hashtag}`), p(i18n.hashtagDescription)),