feature and UI update
- implemented giving multiple categories to articles - testing new fonts -minor bug fixes
This commit is contained in:
parent
edd35f992d
commit
67f6671444
84
css/main.css
84
css/main.css
@ -2,7 +2,7 @@
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: "Archivo", sans-serif;
|
||||
font-family: "Roboto", sans-serif;
|
||||
}
|
||||
|
||||
:root {
|
||||
@ -175,6 +175,17 @@ img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.01em;
|
||||
font-family: "Calistoga", cursive;
|
||||
}
|
||||
|
||||
.overflow-ellipsis {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
@ -454,6 +465,10 @@ h3 {
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
details {
|
||||
border-top: thin rgba(var(--text-color), 0.1) solid;
|
||||
border-bottom: thin rgba(var(--text-color), 0.1) solid;
|
||||
}
|
||||
details summary {
|
||||
display: flex;
|
||||
-webkit-user-select: none;
|
||||
@ -461,7 +476,8 @@ details summary {
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem 0;
|
||||
padding: 0.8rem 0;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
details[open] > summary .down-arrow {
|
||||
transform: rotate(180deg);
|
||||
@ -681,7 +697,7 @@ theme-toggle {
|
||||
grid-template-columns: 5rem 1fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
align-items: flex-start;
|
||||
grid-template-areas: "img title" "img content";
|
||||
grid-template-areas: "img title" "img categories";
|
||||
min-height: 5rem;
|
||||
}
|
||||
.article-card img {
|
||||
@ -691,20 +707,20 @@ theme-toggle {
|
||||
height: 5rem;
|
||||
width: 5rem;
|
||||
}
|
||||
.article-card a {
|
||||
.article-card .article-link {
|
||||
grid-area: title;
|
||||
color: inherit;
|
||||
}
|
||||
.article-card .article-link {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.article-card__title {
|
||||
line-height: 1.4;
|
||||
}
|
||||
.article-card .flex {
|
||||
grid-area: content;
|
||||
.article-card .meta-data {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.article-card__categories {
|
||||
grid-area: categories;
|
||||
}
|
||||
|
||||
.article-card__category {
|
||||
background-color: #256eff08;
|
||||
@ -920,17 +936,17 @@ theme-toggle {
|
||||
}
|
||||
|
||||
#dashboard {
|
||||
height: -webkit-max-content;
|
||||
height: -moz-max-content;
|
||||
height: max-content;
|
||||
height: 100%;
|
||||
padding: 1.5rem 0;
|
||||
grid-template-rows: auto 1fr;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
#publishing_requests,
|
||||
#article_analytics {
|
||||
margin-top: 2rem;
|
||||
align-content: flex-start;
|
||||
padding-bottom: 3rem;
|
||||
}
|
||||
|
||||
.request-card,
|
||||
@ -947,7 +963,7 @@ theme-toggle {
|
||||
|
||||
#preview_popup h1,
|
||||
#article h1 {
|
||||
font-weight: 800;
|
||||
font-weight: 700;
|
||||
font-size: 1.7rem;
|
||||
line-height: 1.1;
|
||||
}
|
||||
@ -1038,6 +1054,50 @@ theme-toggle {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
#edit_category {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.category-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.3rem 0.5rem;
|
||||
border-radius: 0.3rem;
|
||||
font-size: 0.75rem;
|
||||
border: solid thin rgba(var(--text-color), 0.2);
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.category-chip:focus {
|
||||
outline: solid var(--accent-color);
|
||||
}
|
||||
.category-chip input {
|
||||
display: none;
|
||||
}
|
||||
.category-chip span {
|
||||
transition: transform 0.2s;
|
||||
transform: translateX(-0.8rem);
|
||||
}
|
||||
.category-chip .icon {
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s, transform 0.2s;
|
||||
margin-right: 0.3rem;
|
||||
fill: var(--accent-color);
|
||||
transform: translateX(0.5rem);
|
||||
}
|
||||
.category-chip input:checked ~ .icon {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
.category-chip input:checked ~ span {
|
||||
color: var(--accent-color);
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
#user_popup {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
2
css/main.min.css
vendored
2
css/main.min.css
vendored
File diff suppressed because one or more lines are too long
@ -2,7 +2,7 @@
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: "Archivo", sans-serif;
|
||||
font-family: "Roboto", sans-serif;
|
||||
}
|
||||
|
||||
:root {
|
||||
@ -165,6 +165,17 @@ img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.01em;
|
||||
font-family: "Calistoga", cursive;
|
||||
}
|
||||
|
||||
.overflow-ellipsis {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
@ -437,11 +448,14 @@ h3 {
|
||||
}
|
||||
}
|
||||
details {
|
||||
border-top: thin rgba(var(--text-color), 0.1) solid;
|
||||
border-bottom: thin rgba(var(--text-color), 0.1) solid;
|
||||
summary {
|
||||
display: flex;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem 0;
|
||||
padding: 0.8rem 0;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
&[open] > summary {
|
||||
@ -642,7 +656,7 @@ theme-toggle {
|
||||
grid-template-columns: 5rem 1fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
align-items: flex-start;
|
||||
grid-template-areas: "img title" "img content";
|
||||
grid-template-areas: "img title" "img categories";
|
||||
min-height: 5rem;
|
||||
img {
|
||||
left: 0;
|
||||
@ -651,20 +665,20 @@ theme-toggle {
|
||||
height: 5rem;
|
||||
width: 5rem;
|
||||
}
|
||||
a {
|
||||
.article-link {
|
||||
grid-area: title;
|
||||
color: inherit;
|
||||
}
|
||||
.article-link {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
&__title {
|
||||
line-height: 1.4;
|
||||
}
|
||||
.flex {
|
||||
grid-area: content;
|
||||
.meta-data {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
&__categories {
|
||||
grid-area: categories;
|
||||
}
|
||||
}
|
||||
.article-card__category {
|
||||
background-color: #256eff08;
|
||||
@ -877,14 +891,16 @@ theme-toggle {
|
||||
}
|
||||
|
||||
#dashboard {
|
||||
height: max-content;
|
||||
height: 100%;
|
||||
padding: 1.5rem 0;
|
||||
grid-template-rows: auto 1fr;
|
||||
align-items: flex-start;
|
||||
}
|
||||
#publishing_requests,
|
||||
#article_analytics {
|
||||
margin-top: 2rem;
|
||||
align-content: flex-start;
|
||||
padding-bottom: 3rem;
|
||||
}
|
||||
|
||||
.request-card,
|
||||
@ -901,7 +917,7 @@ theme-toggle {
|
||||
#preview_popup,
|
||||
#article {
|
||||
h1 {
|
||||
font-weight: 800;
|
||||
font-weight: 700;
|
||||
font-size: 1.7rem;
|
||||
line-height: 1.1;
|
||||
}
|
||||
@ -984,6 +1000,49 @@ theme-toggle {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
#edit_category {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
border: none;
|
||||
}
|
||||
.category-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.3rem 0.5rem;
|
||||
border-radius: 0.3rem;
|
||||
font-size: 0.75rem;
|
||||
border: solid thin rgba(var(--text-color), 0.2);
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
&:focus {
|
||||
outline: solid var(--accent-color);
|
||||
}
|
||||
input {
|
||||
display: none;
|
||||
}
|
||||
span {
|
||||
transition: transform 0.2s;
|
||||
transform: translateX(-0.8rem);
|
||||
}
|
||||
.icon {
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s, transform 0.2s;
|
||||
margin-right: 0.3rem;
|
||||
fill: var(--accent-color);
|
||||
transform: translateX(0.5rem);
|
||||
}
|
||||
input:checked ~ .icon {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
input:checked ~ span {
|
||||
color: var(--accent-color);
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
#user_popup {
|
||||
overflow: hidden;
|
||||
header {
|
||||
|
||||
255
index.html
255
index.html
@ -10,8 +10,11 @@
|
||||
<link rel="stylesheet" href="css/main.min.css">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<!-- <link
|
||||
href="https://fonts.googleapis.com/css2?family=Archivo:ital,wght@0,400..900;1,400..900&family=Noto+Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap"
|
||||
rel="stylesheet"> -->
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Archivo:wght@400..900&family=Noto+Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap"
|
||||
href="https://fonts.googleapis.com/css2?family=Calistoga&family=Roboto:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap"
|
||||
rel="stylesheet">
|
||||
<script src="purify.min.js" defer></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/fuse.js@6.4.6" defer></script>
|
||||
@ -169,7 +172,7 @@
|
||||
</div>
|
||||
<a class="button" href="#/explore?type=recent">See all</a>
|
||||
</div>
|
||||
<ul id="latest_articles_list" class="grid gap-1-5"></ul>
|
||||
<ul id="latest_articles_list" class="grid gap-2"></ul>
|
||||
</section>
|
||||
</article>
|
||||
<article id="explore" class="page page-layout hide">
|
||||
@ -454,12 +457,13 @@
|
||||
</div>
|
||||
<div class="grid gap-0-5">
|
||||
<h5>Select category</h5>
|
||||
<sm-select id="edit_category"></sm-select>
|
||||
<fieldset id="edit_category"></fieldset>
|
||||
</div>
|
||||
<label class="grid gap-0-5">
|
||||
<h5>Select Hero image</h5>
|
||||
<img src="" alt="" id="preview_image">
|
||||
<input id="select_image" type="file" accept="image">
|
||||
<h5>Hero image URL</h5>
|
||||
<!-- <img src="" alt="" id="preview_image">
|
||||
<input id="select_image" type="file" accept="image"> -->
|
||||
<sm-input id="get_hero_image" type="url"></sm-input>
|
||||
</label>
|
||||
</div>
|
||||
<div class="grid gap-1-5">
|
||||
@ -558,29 +562,29 @@
|
||||
<div class="trending-article grid gap-0-5">
|
||||
<a class="article-card--highlight grid gap-0-5" href="" style="color: inherit">
|
||||
<img class="trending-article__image" src="" alt="" loading="lazy">
|
||||
<div class="flex align-center" style="margin-top: 0.3rem;">
|
||||
<time class="trending-article__published h5"></time>
|
||||
<span class="bullet-point"></span>
|
||||
<span class="trending-article__read-time h5"></span>
|
||||
</div>
|
||||
<h3 class="trending-article__title"></h3>
|
||||
<p class="trending-article__summary"></p>
|
||||
</a>
|
||||
<div class="flex align-center" style="margin-top: 0.3rem;">
|
||||
<a href="" class="article-card__category interact"></a>
|
||||
<time class="trending-article__published h5"></time>
|
||||
<span class="bullet-point"></span>
|
||||
<span class="trending-article__read-time h5"></span>
|
||||
</div>
|
||||
<div class="flex align-center trending-article__categories flex-wrap" style="margin-top: 0.3rem;"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template id="article_card_template">
|
||||
<li class="article-card grid">
|
||||
<a href="" class="grid article-link">
|
||||
<img class="article-card__image" src="" alt="" loading="lazy">
|
||||
<div class="flex align-center meta-data">
|
||||
<time class="article-card__published"></time>
|
||||
<span class="bullet-point"></span>
|
||||
<span class="article-card__read-time"></span>
|
||||
</div>
|
||||
<h4 class="article-card__title"></h4>
|
||||
</a>
|
||||
<div class="flex align-center" style="margin-top: 0.3rem;">
|
||||
<a href="" class="article-card__category interact"></a>
|
||||
<time class="article-card__published"></time>
|
||||
<span class="bullet-point"></span>
|
||||
<span class="article-card__read-time"></span>
|
||||
</div>
|
||||
<div class="flex align-center article-card__categories flex-wrap" style="margin-top: 0.3rem;"></div>
|
||||
</li>
|
||||
</template>
|
||||
<template id="article_row_template">
|
||||
@ -589,13 +593,14 @@
|
||||
<h4 class="article-card__title"></h4>
|
||||
<div class="flex space-between align-center gap-0-5">
|
||||
<div class="flex align-center">
|
||||
<span class="article-row__votes"></span>
|
||||
<svg class="icon button__icon--right" xmlns="http://www.w3.org/2000/svg" height="24px"
|
||||
viewBox="0 0 24 24" width="24px" fill="#000000">
|
||||
<svg class="icon button__icon--left" style="fill: var(--like-color);"
|
||||
xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px"
|
||||
fill="#000000">
|
||||
<path d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
|
||||
</svg>
|
||||
<span class="article-row__votes"></span>
|
||||
</div>
|
||||
<div class="flex gap-0-5">
|
||||
<button class="icon-only edit-article">
|
||||
@ -1007,7 +1012,7 @@
|
||||
}
|
||||
if (targetPage === 'article') {
|
||||
setTimeout(() => {
|
||||
getRef('article').scroll({ top: 0 })
|
||||
window.scroll({ top: 0 })
|
||||
}, 0)
|
||||
mobileQuery.addListener(handleMobileChange)
|
||||
handleMobileChange(mobileQuery)
|
||||
@ -1121,10 +1126,17 @@
|
||||
const relativeTime = new RelativeTime({ style: 'narrow' });
|
||||
const render = {
|
||||
articleCard(details) {
|
||||
const { uid: articleID, category, title, readTime, published, heroImage } = details
|
||||
const { uid: articleID, categories, title, readTime, published, heroImage } = details
|
||||
const clone = getRef('article_card_template').content.cloneNode(true).firstElementChild
|
||||
clone.querySelector('.article-card__category').textContent = category
|
||||
clone.querySelector('.article-card__category').href = `#/explore?type=category&query=${category}`
|
||||
const frag = document.createDocumentFragment()
|
||||
categories.forEach(category => {
|
||||
frag.append(createElement('a', {
|
||||
className: 'article-card__category interact',
|
||||
attributes: { href: `#/explore?type=category&query=${category}` },
|
||||
textContent: category
|
||||
}))
|
||||
})
|
||||
clone.querySelector('.article-card__categories').append(frag)
|
||||
clone.querySelector('.article-link').href = `#/article?articleID=${articleID}`
|
||||
clone.querySelector('.article-card__image').src = heroImage
|
||||
clone.querySelector('.article-card__title').textContent = title
|
||||
@ -1133,10 +1145,17 @@
|
||||
return clone
|
||||
},
|
||||
trendingArticleCard(details) {
|
||||
const { uid: articleID, category, title, readTime, published, summary, heroImage } = details
|
||||
const { uid: articleID, categories, title, readTime, published, summary, heroImage } = details
|
||||
const clone = getRef('trending_article_template').content.cloneNode(true).firstElementChild
|
||||
clone.querySelector('.article-card__category').textContent = category
|
||||
clone.querySelector('.article-card__category').href = `#/explore?type=category&query=${category}`
|
||||
const frag = document.createDocumentFragment()
|
||||
categories.forEach(category => {
|
||||
frag.append(createElement('a', {
|
||||
className: 'article-card__category interact',
|
||||
attributes: { href: `#/explore?type=category&query=${category}` },
|
||||
textContent: category
|
||||
}))
|
||||
})
|
||||
clone.querySelector('.trending-article__categories').append(frag)
|
||||
clone.querySelector('.article-card--highlight').href = `#/article?articleID=${articleID}`
|
||||
clone.querySelector('.trending-article__image').src = heroImage
|
||||
clone.querySelector('.trending-article__title').textContent = title
|
||||
@ -1146,7 +1165,7 @@
|
||||
return clone
|
||||
},
|
||||
requestCard(details) {
|
||||
const { senderID, message: { articleID, category, title }, time, vectorClock } = details
|
||||
const { senderID, message: { articleID, title }, time, vectorClock } = details
|
||||
const { publishedVc, rejectedVc, approvedID } = floGlobals.appObjects.adminData
|
||||
const shouldRender = approvedID.hasOwnProperty(senderID) && !publishedVc.hasOwnProperty(vectorClock) && !rejectedVc.hasOwnProperty(vectorClock)
|
||||
if (shouldRender) {
|
||||
@ -1159,7 +1178,7 @@
|
||||
}
|
||||
},
|
||||
articleRow(articleID) {
|
||||
const { category, title, published, votes } = floGlobals.appObjects.rmTimes.articles[articleID]
|
||||
const { title, published, votes } = floGlobals.appObjects.rmTimes.articles[articleID]
|
||||
const clone = getRef('article_row_template').content.cloneNode(true).firstElementChild
|
||||
clone.dataset.articleId = articleID
|
||||
clone.querySelector('.article-card__title').textContent = title
|
||||
@ -1186,7 +1205,7 @@
|
||||
async homepage() {
|
||||
const frag = document.createDocumentFragment()
|
||||
// Render article topics
|
||||
categories.forEach(({ title, icon }) => frag.append(createElement('a', {
|
||||
allCategories.forEach(({ title, icon }) => frag.append(createElement('a', {
|
||||
attributes: { href: `#/explore?type=category&query=${title.toLowerCase()}` },
|
||||
className: 'category interact',
|
||||
innerHTML: `${icon}<div>${title}</div>`,
|
||||
@ -1219,9 +1238,16 @@
|
||||
async article(articleID, firstLoad = true) {
|
||||
const frag = document.createDocumentFragment()
|
||||
const [allArticles, allImages] = await Promise.all([compactIDB.readData('appObjects', 'articlesContent'), compactIDB.readAllData('images')])
|
||||
const { title, published, readTime, contributors, updated, summary, category } = floGlobals.appObjects.rmTimes.articles[articleID]
|
||||
getRef('article_category').textContent = category
|
||||
getRef('article_category').href = `#/explore?type=category&query=${category}`
|
||||
const { title, published, readTime, contributors, updated, summary, categories } = floGlobals.appObjects.rmTimes.articles[articleID]
|
||||
categories.forEach(category => {
|
||||
frag.append(createElement('a', {
|
||||
className: 'article-card__category interact',
|
||||
attributes: { href: `#/explore?type=category&query=${category}` },
|
||||
textContent: category
|
||||
}))
|
||||
})
|
||||
getRef('article_category').innerHTML = ''
|
||||
getRef('article_category').append(frag)
|
||||
getRef('article_title').textContent = title
|
||||
getRef('article_summary').textContent = summary
|
||||
getRef('published_time').textContent = `${getFormattedTime(published, 'date-only')}${updated ? `, Updated ${relativeTime.from(updated)}` : ''}`
|
||||
@ -1253,8 +1279,8 @@
|
||||
getRef('article_contributors').innerHTML = ''
|
||||
getRef('article_contributors').append(frag)
|
||||
|
||||
const fuse = new Fuse(getArrayOfObj(floGlobals.appObjects.rmTimes.articles), { keys: ['category'], threshold: 0 })
|
||||
const searchResult = fuse.search(category).map(v => v.item).filter(v => v.uid !== articleID)
|
||||
const fuse = new Fuse(getArrayOfObj(floGlobals.appObjects.rmTimes.articles), { keys: ['categories'], threshold: 0, useExtendedSearch: true })
|
||||
const searchResult = fuse.search(categories.join('|')).map(v => v.item).filter(v => v.uid !== articleID)
|
||||
let randomNumbers = []
|
||||
if (searchResult.length > 3) {
|
||||
// generate non-repeating random numbers
|
||||
@ -1311,7 +1337,7 @@
|
||||
}))
|
||||
})
|
||||
} else {
|
||||
const options = (type === 'category') ? { keys: ['category'], threshold: 0 } : { keys: ['title', 'category', 'tags'], threshold: 0.3 }
|
||||
const options = (type === 'category') ? { keys: ['categories'], threshold: 0 } : { keys: ['title', 'categories', 'tags'], threshold: 0.3 }
|
||||
const fuse = new Fuse(sortedByTime, options)
|
||||
const searchResult = fuse.search(query).map(v => v.item)
|
||||
searchResult.forEach(articleDetail => {
|
||||
@ -1384,7 +1410,7 @@
|
||||
return arr
|
||||
}
|
||||
|
||||
const categories = [
|
||||
const allCategories = [
|
||||
{
|
||||
title: 'Culture',
|
||||
icon: `<svg class="icon" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><g><g><g><path d="M12,22C6.49,22,2,17.51,2,12S6.49,2,12,2s10,4.04,10,9c0,3.31-2.69,6-6,6h-1.77c-0.28,0-0.5,0.22-0.5,0.5 c0,0.12,0.05,0.23,0.13,0.33c0.41,0.47,0.64,1.06,0.64,1.67C14.5,20.88,13.38,22,12,22z M12,4c-4.41,0-8,3.59-8,8s3.59,8,8,8 c0.28,0,0.5-0.22,0.5-0.5c0-0.16-0.08-0.28-0.14-0.35c-0.41-0.46-0.63-1.05-0.63-1.65c0-1.38,1.12-2.5,2.5-2.5H16 c2.21,0,4-1.79,4-4C20,7.14,16.41,4,12,4z"/><circle cx="6.5" cy="11.5" r="1.5"/><circle cx="9.5" cy="7.5" r="1.5"/><circle cx="14.5" cy="7.5" r="1.5"/><circle cx="17.5" cy="11.5" r="1.5"/></g></g></g></g></svg>`
|
||||
@ -1429,6 +1455,10 @@
|
||||
title: 'Tech',
|
||||
icon: `<svg class="icon" xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><path d="M19.93,8.35l-3.6,1.68L14,7.7V6.3l2.33-2.33l3.6,1.68c0.38,0.18,0.82,0.01,1-0.36c0.18-0.38,0.01-0.82-0.36-1l-3.92-1.83 c-0.38-0.18-0.83-0.1-1.13,0.2L13.78,4.4C13.6,4.16,13.32,4,13,4c-0.55,0-1,0.45-1,1v1H8.82C8.4,4.84,7.3,4,6,4C4.34,4,3,5.34,3,7 c0,1.1,0.6,2.05,1.48,2.58L7.08,18H6c-1.1,0-2,0.9-2,2v1h13v-1c0-1.1-0.9-2-2-2h-1.62L8.41,8.77C8.58,8.53,8.72,8.28,8.82,8H12v1 c0,0.55,0.45,1,1,1c0.32,0,0.6-0.16,0.78-0.4l1.74,1.74c0.3,0.3,0.75,0.38,1.13,0.2l3.92-1.83c0.38-0.18,0.54-0.62,0.36-1 C20.75,8.34,20.31,8.17,19.93,8.35z M6,8C5.45,8,5,7.55,5,7c0-0.55,0.45-1,1-1s1,0.45,1,1C7,7.55,6.55,8,6,8z M11.11,18H9.17 l-2.46-8h0.1L11.11,18z"/></g></svg>`
|
||||
},
|
||||
{
|
||||
title: 'World',
|
||||
icon: `<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM4 12c0-.61.08-1.21.21-1.78L8.99 15v1c0 1.1.9 2 2 2v1.93C7.06 19.43 4 16.07 4 12zm13.89 5.4c-.26-.81-1-1.4-1.9-1.4h-1v-3c0-.55-.45-1-1-1h-6v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41C17.92 5.77 20 8.65 20 12c0 2.08-.81 3.98-2.11 5.4z"/></svg>`
|
||||
},
|
||||
]
|
||||
|
||||
const headingObserver = new IntersectionObserver(entries => {
|
||||
@ -1504,7 +1534,7 @@
|
||||
const searchKey = e.target.value.trim()
|
||||
if (searchKey !== '') {
|
||||
const options = {
|
||||
keys: ['title', 'tags', 'category'],
|
||||
keys: ['title', 'tags', 'categories'],
|
||||
threshold: 0.3
|
||||
}
|
||||
const fuse = new Fuse(getArrayOfObj(floGlobals.appObjects.rmTimes.articles).sort((a, b) => b.published - a.published), options)
|
||||
@ -1537,7 +1567,7 @@
|
||||
} else {
|
||||
getRef('search_suggestions').classList.add('hide')
|
||||
}
|
||||
}, 100))
|
||||
}, 200))
|
||||
|
||||
getRef('search_articles').addEventListener('keyup', e => {
|
||||
// detect enter key press
|
||||
@ -1865,10 +1895,15 @@
|
||||
getRef('approved_ids').innerHTML = ''
|
||||
getRef('approved_ids').append(frag)
|
||||
|
||||
categories.forEach(({ title }) => {
|
||||
frag.append(createElement('sm-option', {
|
||||
textContent: title,
|
||||
attributes: { value: title.toLowerCase() }
|
||||
allCategories.forEach(({ title }) => {
|
||||
frag.append(createElement('label', {
|
||||
className: 'category-chip interact',
|
||||
attributes: { tabindex: 0 },
|
||||
innerHTML: `
|
||||
<input type="checkbox" name="category" value="${title.toLowerCase()}" />
|
||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/></svg>
|
||||
<span class="checkmark">${title}</span>
|
||||
`
|
||||
}))
|
||||
})
|
||||
getRef('edit_category').innerHTML = ''
|
||||
@ -1880,7 +1915,7 @@
|
||||
const isPublished = floGlobals.appObjects.rmTimes.articles.hasOwnProperty(articleID)
|
||||
getConfirmation(`${isPublished ? 'Update' : 'Publish'} article?`).then(async res => {
|
||||
if (res) {
|
||||
const { title, category, summary, published, tags, contributors, heroImage } = getArticleMetaData()
|
||||
const { title, categories, summary, published, tags, contributors, heroImage } = getArticleMetaData()
|
||||
floGlobals.appObjects.adminData.publishedVc[vectorClock] = true
|
||||
floGlobals.appObjects.articlesContent[articleID] = content
|
||||
if (isPublished) {
|
||||
@ -1893,17 +1928,18 @@
|
||||
}
|
||||
floGlobals.appObjects.adminData.articleVotes[articleID] = { votes: {} }
|
||||
}
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].category = category
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].categories = categories
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].contributors = contributors
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].title = title
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].heroImage = heroImage
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].tags = tags
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].readTime = readTime
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].summary = summary
|
||||
compactIDB.writeData('images', { heroImage }, articleID)
|
||||
const response = await floCloudAPI.sendApplicationData(heroImage, 'images')
|
||||
for (const key in response) {
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].heroImage = response[key].vectorClock
|
||||
}
|
||||
// compactIDB.writeData('images', { heroImage }, articleID)
|
||||
// const response = await floCloudAPI.sendApplicationData(heroImage, 'images')
|
||||
// for (const key in response) {
|
||||
// floGlobals.appObjects.rmTimes.articles[articleID].heroImage = response[key].vectorClock
|
||||
// }
|
||||
Promise.all([
|
||||
floCloudAPI.updateObjectData('rmTimes'),
|
||||
floCloudAPI.updateObjectData('adminData'),
|
||||
@ -1964,18 +2000,28 @@
|
||||
}
|
||||
}
|
||||
|
||||
function setSelectedCategories(categories = []) {
|
||||
getRef('edit_category').querySelectorAll(`[name="category"]`).forEach(checkbox => {
|
||||
checkbox.checked = categories.includes(checkbox.value)
|
||||
})
|
||||
}
|
||||
function getSelectedCategories() {
|
||||
return [...getRef('edit_category').querySelectorAll('input:checked')].map(input => input.value)
|
||||
}
|
||||
|
||||
async function setArticleMetaData(details, articleID) {
|
||||
const { category, title, tags, summary, published, contributors } = details
|
||||
const articleImages = await compactIDB.readData('images', articleID)
|
||||
if (articleImages) {
|
||||
getRef('preview_image').src = articleImages.heroImage.thumbnail
|
||||
getRef('select_image').value = '';
|
||||
} else {
|
||||
getRef('preview_image').src = ''
|
||||
}
|
||||
const { categories, title, tags, summary, published, contributors, heroImage } = details
|
||||
// const articleImages = await compactIDB.readData('images', articleID)
|
||||
// if (articleImages) {
|
||||
// getRef('preview_image').src = articleImages.heroImage.thumbnail
|
||||
// getRef('select_image').value = '';
|
||||
// } else {
|
||||
// getRef('preview_image').src = ''
|
||||
// }
|
||||
getRef('get_hero_image').value = heroImage
|
||||
getRef('edit_title').value = title;
|
||||
getRef('edit_summary').value = summary || '';
|
||||
getRef('edit_category').value = category || '';
|
||||
setSelectedCategories(categories || '');
|
||||
getRef('edit_contributors').value = contributors || [];
|
||||
getRef('edit_tags').value = tags || [];
|
||||
const now = Date.now()
|
||||
@ -1984,8 +2030,8 @@
|
||||
function getArticleMetaData() {
|
||||
return {
|
||||
title: getRef('edit_title').value.trim(),
|
||||
category: getRef('edit_category').value,
|
||||
heroImage: currentSelectedImage,
|
||||
categories: getSelectedCategories(),
|
||||
heroImage: getRef('get_hero_image').value.trim(),
|
||||
summary: getRef('edit_summary').value.trim(),
|
||||
published: new Date(getRef('edit_published').value).getTime(),
|
||||
contributors: getRef('edit_contributors').value,
|
||||
@ -2030,17 +2076,18 @@
|
||||
function updateArticleMetaData(articleID) {
|
||||
getConfirmation('Update article meta data?').then(async res => {
|
||||
if (res) {
|
||||
const { title, category, summary, published, tags, contributors, heroImage } = getArticleMetaData()
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].category = category
|
||||
const { title, categories, summary, published, tags, contributors, heroImage } = getArticleMetaData()
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].categories = categories
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].title = title
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].heroImage = heroImage
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].tags = tags
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].summary = summary
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].contributors = contributors
|
||||
compactIDB.writeData('images', { heroImage }, articleID)
|
||||
const response = await floCloudAPI.sendApplicationData(heroImage, 'images')
|
||||
for (const key in response) {
|
||||
floGlobals.appObjects.rmTimes.articles[articleID].heroImage = response[key].vectorClock
|
||||
}
|
||||
// compactIDB.writeData('images', { heroImage }, articleID)
|
||||
// const response = await floCloudAPI.sendApplicationData(heroImage, 'images')
|
||||
// for (const key in response) {
|
||||
// floGlobals.appObjects.rmTimes.articles[articleID].heroImage = response[key].vectorClock
|
||||
// }
|
||||
floCloudAPI.updateObjectData('rmTimes').then(() => {
|
||||
notify(`Updated article meta data`, 'success')
|
||||
hidePopup()
|
||||
@ -2236,44 +2283,44 @@
|
||||
}
|
||||
}
|
||||
|
||||
let currentSelectedImage = {}
|
||||
getRef('select_image').addEventListener('change', function (e) {
|
||||
currentSelectedImage = {}
|
||||
if (this.files.length === 0) return
|
||||
const selectedFile = this.files[0]
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
// show selected image in the preview
|
||||
getRef('preview_image').src = e.target.result;
|
||||
getRef('preview_image').addEventListener('load', function (event) {
|
||||
// Dynamically create a canvas element
|
||||
const canvas = createElement("canvas");
|
||||
const context = canvas.getContext("2d");
|
||||
const originalWidth = getRef('preview_image').width;
|
||||
const originalHeight = getRef('preview_image').height;
|
||||
const resizingFactor = parseFloat((1 / (originalWidth / 600)).toFixed(2));
|
||||
// let currentSelectedImage = {}
|
||||
// getRef('select_image').addEventListener('change', function (e) {
|
||||
// currentSelectedImage = {}
|
||||
// if (this.files.length === 0) return
|
||||
// const selectedFile = this.files[0]
|
||||
// const reader = new FileReader();
|
||||
// reader.onload = function (e) {
|
||||
// // show selected image in the preview
|
||||
// getRef('preview_image').src = e.target.result;
|
||||
// getRef('preview_image').addEventListener('load', function (event) {
|
||||
// // Dynamically create a canvas element
|
||||
// const canvas = createElement("canvas");
|
||||
// const context = canvas.getContext("2d");
|
||||
// const originalWidth = getRef('preview_image').width;
|
||||
// const originalHeight = getRef('preview_image').height;
|
||||
// const resizingFactor = parseFloat((1 / (originalWidth / 600)).toFixed(2));
|
||||
|
||||
const canvasWidth = originalWidth * resizingFactor;
|
||||
const canvasHeight = originalHeight * resizingFactor;
|
||||
// const canvasWidth = originalWidth * resizingFactor;
|
||||
// const canvasHeight = originalHeight * resizingFactor;
|
||||
|
||||
canvas.width = canvasWidth;
|
||||
canvas.height = canvasHeight;
|
||||
// canvas.width = canvasWidth;
|
||||
// canvas.height = canvasHeight;
|
||||
|
||||
context.drawImage(
|
||||
getRef('preview_image'),
|
||||
0,
|
||||
0,
|
||||
originalWidth * resizingFactor,
|
||||
originalHeight * resizingFactor
|
||||
);
|
||||
currentSelectedImage['thumbnail'] = canvas.toDataURL(selectedFile.type);
|
||||
}, { once: true });
|
||||
currentSelectedImage.name = selectedFile.name
|
||||
currentSelectedImage.type = selectedFile.type
|
||||
currentSelectedImage.full = e.target.result
|
||||
}
|
||||
reader.readAsDataURL(this.files[0]);
|
||||
})
|
||||
// context.drawImage(
|
||||
// getRef('preview_image'),
|
||||
// 0,
|
||||
// 0,
|
||||
// originalWidth * resizingFactor,
|
||||
// originalHeight * resizingFactor
|
||||
// );
|
||||
// currentSelectedImage['thumbnail'] = canvas.toDataURL(selectedFile.type);
|
||||
// }, { once: true });
|
||||
// currentSelectedImage.name = selectedFile.name
|
||||
// currentSelectedImage.type = selectedFile.type
|
||||
// currentSelectedImage.full = e.target.result
|
||||
// }
|
||||
// reader.readAsDataURL(this.files[0]);
|
||||
// })
|
||||
|
||||
</script>
|
||||
<script id="onLoadStartUp">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user