Skip to content

Commit

Permalink
client: Use real React for NavTags
Browse files Browse the repository at this point in the history
  • Loading branch information
jtojnar committed Jan 4, 2021
1 parent ee263f9 commit 6e8240f
Show file tree
Hide file tree
Showing 6 changed files with 13,501 additions and 115 deletions.
1 change: 0 additions & 1 deletion assets/js/selfoss-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,6 @@ var selfoss = {
}

$(`#nav-sources a[data-source-id="${selfoss.filter.source}"]`).addClass('active');
$('#nav-tags > li > a').removeClass('active');
}

selfoss.sourcesNavLoaded = true;
Expand Down
1 change: 0 additions & 1 deletion assets/js/selfoss-events-navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ selfoss.events.navigation = function() {
return;
}

$('#nav-tags > li > a').removeClass('active');
$('#nav-sources > li > a').removeClass('active');
$(this).addClass('active');

Expand Down
101 changes: 19 additions & 82 deletions assets/js/selfoss-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import React from 'jsx-dom';
import locales from './locales';
import selfoss from './selfoss-base';
import { initIcons } from './icons';
import NavTags from './templates/NavTags';
import { updateTag } from './requests/tags';
import { filterTypeToString } from './helpers/uri';
import * as NavTags from './templates/NavTags';

/**
* ui change functions
Expand Down Expand Up @@ -160,6 +158,8 @@ selfoss.ui = {
document.body.appendChild(script);
}

NavTags.anchor(document.querySelector('#nav-tags'), selfoss.tags, selfoss.filter);

selfoss.tags.addEventListener('statechange', (event) => {
if (event.state === 'loading') {
$('#nav-tags').addClass('loading');
Expand All @@ -168,74 +168,18 @@ selfoss.ui = {
}
});

selfoss.tags.addEventListener('change', (event) => {
$('#nav-tags').html(<NavTags tags={event.tags} />);
function setupTags() {
if (selfoss.filter.tag) {
if (!selfoss.db.isValidTag(selfoss.filter.tag)) {
selfoss.ui.showError(selfoss.ui._('error_unknown_tag') + ' ' + selfoss.filter.tag);
}

$('#nav-tags li:first a').removeClass('active');
$('#nav-tags > li > a').filter(function() {
if ($('.tag', this)) {
return $('.tag', this).html() == selfoss.filter.tag;
} else {
return false;
}
}).addClass('active');
} else {
$('.nav-tags-all').addClass('active');
}
}

// init colorpicker
$('.color').spectrum('destroy');
$('.color').spectrum({
showPaletteOnly: true,
color: 'blanchedalmond',
palette: [
['#ffccc9', '#ffce93', '#fffc9e', '#ffffc7', '#9aff99', '#96fffb', '#cdffff', '#cbcefb', '#fffe65', '#cfcfcf', '#fd6864', '#fe996b', '#fcff2f', '#67fd9a', '#38fff8', '#68fdff', '#9698ed', '#c0c0c0', '#fe0000', '#f8a102', '#ffcc67', '#f8ff00', '#34ff34', '#68cbd0', '#34cdf9', '#6665cd', '#9b9b9b', '#cb0000', '#f56b00', '#ffcb2f', '#ffc702', '#32cb00', '#00d2cb', '#3166ff', '#6434fc', '#656565', '#9a0000', '#ce6301', '#cd9934', '#999903', '#009901', '#329a9d', '#3531ff', '#6200c9', '#343434', '#680100', '#963400', '#986536', '#646809', '#036400', '#34696d', '#00009b', '#303498', '#000000', '#330001', '#643403', '#663234', '#343300', '#013300', '#003532', '#010066', '#340096']
],
change: function(color) {
$(this).css('backgroundColor', color.toHexString());

const tagName = $(this).parent().find('.tag').html();

updateTag(
tagName,
color.toHexString()
).then(() => {
selfoss.ui.beforeReloadList();
selfoss.dbOnline.reloadList();
selfoss.ui.afterReloadList();
}).catch((error) => {
selfoss.ui.showError(selfoss.ui._('error_saving_color') + ' ' + error.message);
});

}
});

// tag
$('#nav-tags > li > a').unbind('click').click(function(e) {
e.preventDefault();

if (!selfoss.db.online) {
return;
}

$('#nav-tags > li > a').removeClass('active');
$('#nav-sources > li > a').removeClass('active');
$(this).addClass('active');

if ($(this).hasClass('nav-tags-all') == false) {
selfoss.events.setHash(filterTypeToString(selfoss.filter.type),
'tag-' + $(this).find('span').html());
} else {
selfoss.events.setHash(filterTypeToString(selfoss.filter.type), 'all');
}
// It might happen that tags are loaded before the event listener is set up.
setupTags();

selfoss.ui.hideMobileNav();
});
});
selfoss.tags.addEventListener('change', setupTags);
},

showLogin: function(error = '') {
Expand Down Expand Up @@ -760,31 +704,24 @@ selfoss.ui = {


refreshTagSourceUnread: function(tagCounts, sourceCounts, diff = true) {
$('#nav-tags > li > a > span.tag').each(function() {
const tag = this.textContent;

if (!(tag in tagCounts)) {
return;
const tags = selfoss.tags.tags.map((tag) => {
if (!(tag.tag in tagCounts)) {
return tag;
}

const tagsCountEl = $(this).next();

var unreadCount = 0;
let count;
if (diff) {
if (tagsCountEl.html() != '') {
unreadCount = parseInt(tagsCountEl.html());
}
unreadCount = unreadCount + tagCounts[tag];
count = tag.count + tagCounts[tag.tag];
} else {
unreadCount = tagCounts[tag];
count = tagCounts[tag.tag];
}

if (unreadCount > 0) {
tagsCountEl.html(unreadCount);
} else {
tagsCountEl.html('');
}
return {
...tag,
count
};
});
selfoss.tags.update(tags);

if (selfoss.sourcesNavLoaded) {
$('#nav-sources a').each(function() {
Expand Down
101 changes: 93 additions & 8 deletions assets/js/templates/NavTags.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,103 @@
import React from 'jsx-dom';
import React from 'react';
import ReactDOM from 'react-dom';
import { filterTypeToString } from '../helpers/uri';
import classNames from 'classnames';
import { updateTag } from '../requests/tags';

function handleClick(e, tag) {
e.preventDefault();

if (!selfoss.db.online) {
return;
}

if (tag !== null) {
selfoss.events.setHash(filterTypeToString(selfoss.filter.type), `tag-${tag}`);
} else {
selfoss.events.setHash(filterTypeToString(selfoss.filter.type), 'all');
}

selfoss.ui.hideMobileNav();
}

function ColorChooser({tag}) {
const colorChooser = React.useRef(null);

React.useLayoutEffect(() => {
// init colorpicker
$(colorChooser.current).spectrum({
showPaletteOnly: true,
color: 'blanchedalmond',
palette: [
['#ffccc9', '#ffce93', '#fffc9e', '#ffffc7', '#9aff99', '#96fffb', '#cdffff', '#cbcefb', '#fffe65', '#cfcfcf', '#fd6864', '#fe996b', '#fcff2f', '#67fd9a', '#38fff8', '#68fdff', '#9698ed', '#c0c0c0', '#fe0000', '#f8a102', '#ffcc67', '#f8ff00', '#34ff34', '#68cbd0', '#34cdf9', '#6665cd', '#9b9b9b', '#cb0000', '#f56b00', '#ffcb2f', '#ffc702', '#32cb00', '#00d2cb', '#3166ff', '#6434fc', '#656565', '#9a0000', '#ce6301', '#cd9934', '#999903', '#009901', '#329a9d', '#3531ff', '#6200c9', '#343434', '#680100', '#963400', '#986536', '#646809', '#036400', '#34696d', '#00009b', '#303498', '#000000', '#330001', '#643403', '#663234', '#343300', '#013300', '#003532', '#010066', '#340096']
],
change: function(color) {
updateTag(
tag.tag,
color.toHexString()
).then(() => {
selfoss.ui.beforeReloadList();
selfoss.dbOnline.reloadList();
selfoss.ui.afterReloadList();
}).catch((error) => {
selfoss.ui.showError(selfoss.ui._('error_saving_color') + ' ' + error.message);
});

}
});

return () => {
$(colorChooser.current).spectrum('destroy');
};
}, [tag.tag]);

return (
<span className="color" style={{backgroundColor: tag.color}} ref={colorChooser} />
);
}

function NavTags({tagsRepository, filter}) {
const [currentAllTags, setCurrentAllTags] = React.useState(filter.tag === null && filter.source === null);
const [currentTag, setCurrentTag] = React.useState(filter.tag);
const [tags, setTags] = React.useState(tagsRepository.tags);

React.useEffect(() => {
const filterListener = (event) => {
setCurrentAllTags(event.filter.tag === null && event.filter.source === null);
setCurrentTag(event.filter.tag);
};
const tagsListener = (event) => {
setTags(event.tags);
};

// It might happen that filter changes between creating the component and setting up the event handlers.
filterListener({ filter });

filter.addEventListener('change', filterListener);
tagsRepository.addEventListener('change', tagsListener);

return () => {
filter.removeEventListener('change', filterListener);
tagsRepository.removeEventListener('change', tagsListener);
};
}, [tagsRepository, filter]);

export default function NavTags({tags}) {
return (
<React.Fragment>
<li><a class="active nav-tags-all" href="#">{selfoss.ui._('alltags')}</a></li>
<li><a className={classNames({'nav-tags-all': true, active: currentAllTags})} href="#" onClick={(e) => handleClick(e, null)}>{selfoss.ui._('alltags')}</a></li>
{tags.map(tag =>
<li>
<a href="#">
<span class="tag">{`${tag.tag}`}</span>
<span class="unread">{`${(tag.unread > 0) ? tag.unread : ''}`}</span>
<span class="color" style={`background-color: ${tag.color}`}></span>
<li key={tag.tag}>
<a className={classNames({active: currentTag === tag.tag})} href="#" onClick={(e) => handleClick(e, tag.tag)}>
<span className="tag">{`${tag.tag}`}</span>
<span className="unread">{`${(tag.unread > 0) ? tag.unread : ''}`}</span>
<ColorChooser tag={tag} />
</a>
</li>
)}
</React.Fragment>
);
}

export function anchor(element, tags, filter) {
ReactDOM.render(<NavTags tagsRepository={tags} filter={filter} />, element);
}
Loading

0 comments on commit 6e8240f

Please sign in to comment.