feat: content link states

pull/1016/head
Nick 5 years ago
parent e491af44ad
commit efab00fa0c

@ -71,6 +71,7 @@
single-line, single-line,
solo solo
flat flat
rounded
hide-details, hide-details,
prepend-inner-icon='mdi-magnify', prepend-inner-icon='mdi-magnify',
:loading='searchIsLoading', :loading='searchIsLoading',

@ -21,6 +21,7 @@
.overline.mr-3.animated.fadeInLeft Current Selection .overline.mr-3.animated.fadeInLeft Current Selection
v-chip.mr-3.primary--text( v-chip.mr-3.primary--text(
v-for='tag of tagsSelected' v-for='tag of tagsSelected'
:key='`tagSelected-` + tag.tag'
color='white' color='white'
close close
@click:close='toggleTag(tag.tag)' @click:close='toggleTag(tag.tag)'
@ -38,7 +39,7 @@
template(v-else) template(v-else)
v-icon.mr-3.animated.fadeInRight mdi-arrow-left v-icon.mr-3.animated.fadeInRight mdi-arrow-left
.overline.animated.fadeInRight Select one or more tags .overline.animated.fadeInRight Select one or more tags
v-toolbar(color='grey lighten-4', flat, height='58') v-toolbar(:color='$vuetify.theme.dark ? `grey darken-4-l5` : `grey lighten-4`', flat, height='58')
v-text-field.tags-search( v-text-field.tags-search(
label='Search within results...' label='Search within results...'
solo solo
@ -50,12 +51,29 @@
prepend-icon='mdi-file-document-box-search-outline' prepend-icon='mdi-file-document-box-search-outline'
append-icon='mdi-arrow-right' append-icon='mdi-arrow-right'
) )
template(v-if='locales.length > 1')
v-divider.mx-3(vertical)
.overline Locale
v-select.ml-2(
:items='locales'
v-model='locale'
:background-color='$vuetify.theme.dark ? `grey darken-3` : `white`'
hide-details
label='Locale'
item-text='name'
item-value='code'
rounded
single-line
dense
height='40'
style='max-width: 170px;'
)
v-divider.mx-3(vertical) v-divider.mx-3(vertical)
.overline Order By .overline Order By
v-select.ml-2( v-select.ml-2(
:items='orderByItems' :items='orderByItems'
v-model='orderBy' v-model='orderBy'
background-color='white' :background-color='$vuetify.theme.dark ? `grey darken-3` : `white`'
hide-details hide-details
label='Order By' label='Order By'
rounded rounded
@ -64,12 +82,13 @@
height='40' height='40'
style='max-width: 250px;' style='max-width: 250px;'
) )
v-divider.mx-3(vertical) v-btn-toggle.ml-2(v-model='orderByDirection', rounded, mandatory)
v-btn-toggle(v-model='displayStyle', rounded, mandatory) v-btn(text, height='40'): v-icon(size='20') mdi-chevron-double-up
v-btn(text, height='40'): v-icon(small) mdi-view-list v-btn(text, height='40'): v-icon(size='20') mdi-chevron-double-down
v-btn(text, height='40'): v-icon(small) mdi-cards-variant
v-btn(text, height='40'): v-icon(small) mdi-format-align-justify
v-divider v-divider
.text-center.pt-10
img(src='/svg/icon-price-tag.svg')
.subtitle-2.grey--text Select one or more tags on the left.
nav-footer nav-footer
notify notify
search-results search-results
@ -77,16 +96,25 @@
<script> <script>
import { get } from 'vuex-pathify' import { get } from 'vuex-pathify'
import VueRouter from 'vue-router'
import _ from 'lodash' import _ from 'lodash'
import tagsQuery from 'gql/common/common-pages-query-tags.gql' import tagsQuery from 'gql/common/common-pages-query-tags.gql'
/* global siteLangs */
const router = new VueRouter({
mode: 'history',
base: '/t'
})
export default { export default {
data() { data() {
return { return {
tags: [], tags: [],
selection: [], selection: [],
displayStyle: 0, locale: 'any',
locales: [],
orderBy: 'TITLE', orderBy: 'TITLE',
orderByItems: [ orderByItems: [
{ text: 'Creation Date', value: 'CREATED' }, { text: 'Creation Date', value: 'CREATED' },
@ -95,6 +123,7 @@ export default {
{ text: 'Path', value: 'PATH' }, { text: 'Path', value: 'PATH' },
{ text: 'Title', value: 'TITLE' } { text: 'Title', value: 'TITLE' }
], ],
orderByDirection: 0,
scrollStyle: { scrollStyle: {
vuescroll: {}, vuescroll: {},
scrollPanel: { scrollPanel: {
@ -127,8 +156,27 @@ export default {
return _.filter(this.tags, t => _.includes(this.selection, t.tag)) return _.filter(this.tags, t => _.includes(this.selection, t.tag))
} }
}, },
watch: {
locale (newValue, oldValue) {
this.rebuildURL()
},
orderBy (newValue, oldValue) {
this.rebuildURL()
},
orderByDirection (newValue, oldValue) {
this.rebuildURL()
}
},
router,
created () { created () {
this.$store.commit('page/SET_MODE', 'tags') this.$store.commit('page/SET_MODE', 'tags')
this.locales = _.concat(
[{name: 'Any', code: 'any'}],
(siteLangs.length > 0 ? siteLangs : [])
)
this.selection = _.compact(this.$route.path.split('/'))
}, },
methods: { methods: {
toggleTag (tag) { toggleTag (tag) {
@ -137,9 +185,25 @@ export default {
} else { } else {
this.selection.push(tag) this.selection.push(tag)
} }
this.rebuildURL()
}, },
isSelected (tag) { isSelected (tag) {
return _.includes(this.selection, tag) return _.includes(this.selection, tag)
},
rebuildURL () {
let urlObj = {
path: '/' + this.selection.join('/')
}
if (this.locale !== `any`) {
_.set(urlObj, 'query.lang', this.locale)
}
if (this.orderBy !== `TITLE`) {
_.set(urlObj, 'query.sort', this.orderBy.toLowerCase())
}
if (this.orderByDirection !== 0) {
_.set(urlObj, 'query.dir', this.orderByDirection === 0 ? `asc` : `desc`)
}
this.$router.push(urlObj)
} }
}, },
apollo: { apollo: {

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="256" height="256"><path fill="#f7f7fb" d="M43.5,28L16.9,45.4c-5,2.3-2.3,9.1,0,14.1l17.9,17.9l62.2,23l17-47L43.5,28 M35.3,59.5 c-3.9-1.4-5.9-5.7-4.5-9.6c1.4-3.9,5.7-5.9,9.6-4.5s5.9,5.7,4.5,9.6C43.5,58.9,39.2,61,35.3,59.5"/><path fill="#dfdfe3" d="M30.353,52.448c0-0.846,0.144-1.706,0.446-2.548c1.098-3.057,3.977-4.946,7.048-4.946 c0.847,0,1.709,0.144,2.552,0.446l0,0c3.065,1.1,4.957,3.993,4.946,7.074C45.349,53.317,45.206,54.17,44.9,55l0,0 c-1.091,3.04-3.945,4.986-6.997,4.986c-0.864,0-1.743-0.156-2.603-0.486C32.242,58.402,30.352,55.521,30.353,52.448 M43.5,28 L16.9,45.4c-2.078,0.956-2.826,2.689-2.825,4.746c0,2.894,1.481,6.431,2.825,9.354l17.9,17.9l21.635,8H102.4l11.6-32L43.5,28"/><path fill="#454b54" d="M97,103.5c-0.4,0-0.7-0.1-1-0.2l-62.2-23c-0.4-0.2-0.8-0.4-1.1-0.7L14.8,61.7 c-0.6-0.6-0.9-1.3-0.9-2.1V45.4c0-1,0.5-2,1.4-2.5l26.6-17.5c0.8-0.5,1.8-0.6,2.7-0.3l70.5,25.5c0.7,0.3,1.4,0.8,1.7,1.5 s0.4,1.5,0.1,2.3l-17,47c-0.3,0.8-0.8,1.4-1.6,1.7C97.9,103.4,97.4,103.5,97,103.5z M36.5,74.9l58.7,21.7l15-41.4l-66.3-24L19.9,47 v11.3L36.5,74.9z"/><path fill="#6ec7b0" d="M34.8,27.5L16.9,45.4c-3.9,3.9-3.9,10.2,0,14.1l17.9,17.9h75v-50h-75V27.5z M37.8,60 c-4.1,0-7.5-3.4-7.5-7.5s3.4-7.5,7.5-7.5s7.5,3.4,7.5,7.5S42,60,37.8,60z"/><path fill="#454b54" d="M109.8,80.5h-75c-0.8,0-1.6-0.3-2.1-0.9L14.8,61.7C12.3,59.2,11,56,11,52.5s1.4-6.7,3.8-9.2 l17.9-17.9c0.6-0.6,1.3-0.9,2.1-0.9h75c1.7,0,3,1.3,3,3v50C112.8,79.2,111.5,80.5,109.8,80.5z M36.1,74.5h70.8v-44H36.1L19,47.5 c-1.3,1.3-2,3.1-2,5s0.7,3.6,2,4.9L36.1,74.5z"/><path fill="#fff" d="M89.8 50.5h-25c-1.7 0-3-1.3-3-3s1.3-3 3-3h25c1.7 0 3 1.3 3 3S91.5 50.5 89.8 50.5zM89.8 60.5h-25c-1.7 0-3-1.3-3-3s1.3-3 3-3h25c1.7 0 3 1.3 3 3S91.5 60.5 89.8 60.5z"/><path fill="#454b54" d="M7.8,85.5c-0.8,0-1.5-0.3-2.1-0.9c-1.2-1.2-1.2-3.1,0-4.2l9.1-9.1c1.2-1.2,3.1-1.2,4.2,0 c1.2,1.2,1.2,3.1,0,4.2l-9.1,9.1C9.4,85.2,8.6,85.5,7.8,85.5z"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -10,11 +10,40 @@
} }
@at-root .theme--dark & { @at-root .theme--dark & {
// background-color: darken(mc('grey', '900'), 4%);
color: mc('grey', '300'); color: mc('grey', '300');
}
// ---------------------------------
// LINKS
// ---------------------------------
a { a {
color: mc('blue', '100'); color: mc('blue', '700');
&.is-internal-link.is-invalid-page {
color: mc('red', '700');
@at-root .theme--dark & {
color: mc('red', '200');
}
}
&.is-external-link {
padding-right: 3px;
&::after {
font-family: 'Material Design Icons';
font-size: 24px/1;
padding-left: 3px;
display: inline-block;
content: '\F3CC';
color: mc('grey', '500');
text-decoration: none;
}
}
@at-root .theme--dark & {
color: mc('blue', '200');
} }
} }

@ -22,7 +22,7 @@ module.exports = {
name: _.get(c, 'name', 'Anonymous') || '', name: _.get(c, 'name', 'Anonymous') || '',
profile: _.get(c, 'profile', ''), profile: _.get(c, 'profile', ''),
tier: _.toLower(_.get(c, 'tier', 'backers')), tier: _.toLower(_.get(c, 'tier', 'backers')),
totalDonated: _.get(c, 'totalAmountDonated', 0), totalDonated: Math.ceil(_.get(c, 'totalAmountDonated', 0)),
twitter: _.get(c, 'twitter', '') || '', twitter: _.get(c, 'twitter', '') || '',
website: _.get(c, 'website', '') || '' website: _.get(c, 'website', '') || ''
})) }))

@ -8,6 +8,8 @@ module.exports = async (pageId) => {
try { try {
WIKI.models = require('../core/db').init() WIKI.models = require('../core/db').init()
await WIKI.configSvc.loadFromDb()
await WIKI.configSvc.applyFlags()
const page = await WIKI.models.pages.getPageFromDb(pageId) const page = await WIKI.models.pages.getPageFromDb(pageId)
if (!page) { if (!page) {

@ -1,6 +1,8 @@
const _ = require('lodash') const _ = require('lodash')
const cheerio = require('cheerio') const cheerio = require('cheerio')
/* global WIKI */
module.exports = { module.exports = {
async render() { async render() {
const $ = cheerio.load(this.input) const $ = cheerio.load(this.input)
@ -14,6 +16,98 @@ module.exports = {
renderer.init($, child.config) renderer.init($, child.config)
} }
// --------------------------------
// Detect internal / external links
// --------------------------------
let internalRefs = []
const reservedPrefixes = /^\/[a-z]\//gi
const isHostSet = WIKI.config.host.length > 7 && WIKI.config.host !== 'http://'
if (!isHostSet) {
WIKI.logger.warn('Host is not set. You must set the Site Host under General in the Administration Area!')
}
$('a').each((i, elm) => {
let href = $(elm).attr('href')
// -> Ignore empty links
if (!href || href.length < 1) {
return
}
// -> Strip host from local links
if (isHostSet && href.indexOf(WIKI.config.site.host) === 0) {
href = href.replace(WIKI.config.site.host, '')
}
// -> Assign local / external tag
if (href.indexOf('://') < 0) {
// -> Remove trailing slash
if (_.endsWith('/')) {
href = href.slice(0, -1)
}
// -> Check for system prefix
if (!reservedPrefixes.test(href)) {
$(elm).addClass(`is-internal-link`)
// -> Reformat paths
if (href.indexOf('/') !== 0) {
href = `/${this.page.localeCode}/${this.page.path}/${href}`
} else if (href.charAt(3) !== '/') {
href = `/${this.page.localeCode}${href}`
}
// -> Save internal references
internalRefs.push({
localeCode: href.substring(1, 3),
path: _.head(href.substring(4).split('#'))
})
} else {
$(elm).addClass(`is-system-link`)
}
} else {
$(elm).addClass(`is-external-link`)
}
// -> Update element
$(elm).attr('href', href)
})
// --------------------------------
// Detect internal link states
// --------------------------------
if (internalRefs.length > 0) {
// -> Find matching pages
const results = await WIKI.models.pages.query().column('path', 'localeCode').where(builder => {
internalRefs.forEach((ref, idx) => {
if (idx < 1) {
builder.where(ref)
} else {
builder.orWhere(ref)
}
})
})
// -> Apply tag to internal links for found pages
$('a.is-internal-link').each((i, elm) => {
const href = $(elm).attr('href')
const hrefObj = {
localeCode: href.substring(1, 3),
path: _.head(href.substring(4).split('#'))
}
if (_.some(results, r => {
return r.localeCode === hrefObj.localeCode && r.path === hrefObj.path
})) {
$(elm).addClass(`is-valid-page`)
} else {
$(elm).addClass(`is-invalid-page`)
}
})
}
return $.html('body').replace('<body>', '').replace('</body>', '') return $.html('body').replace('<body>', '').replace('</body>', '')
} }
} }

Loading…
Cancel
Save