Skip to content

Commit

Permalink
Create a Wikibase discovery page (#719)
Browse files Browse the repository at this point in the history
* Create a Wikibase discovery page

* Show first page of results when modifying filters

* Enable empty Wikibase filter

* Adjust empty Wikibase filter positioning

* Fix content width for larger displays

* Update copy for when Wikibase stats are unavailable

* Switch to a masonry grid layout

* Rename components

* Remove semicolons and trailing commas

* Add mock data

* Fix logo url for mock data

* Set page count as the default sort order

* Enter key should open Wikibase

* Update copy for discovery page

* Scroll to top after list is updated

* Adjust content margins and breakpoints

* Fix linting errors

* Switch to a rounded avatar for Wikibase logos

* Simplify diff for merge
  • Loading branch information
AndrewKostka authored Sep 11, 2023
1 parent 5181d01 commit 6dcae41
Show file tree
Hide file tree
Showing 9 changed files with 514 additions and 7 deletions.
17 changes: 12 additions & 5 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<template>
<v-app id="app">
<Navbar></Navbar>

<v-container class="full-height-content">
<router-view v-if="customLayout"/>
<v-container v-else class="full-height-content">
<v-row >
<v-col cols="1"></v-col>
<v-col cols="10">
Expand All @@ -11,9 +11,7 @@
<v-col cols="1"></v-col>
</v-row>
</v-container>

<Foot></Foot>

<Foot :class="{'tall-footer': customLayout}"></Foot>
<Interval
v-if="this.$store.getters.isLoggedIn && !this.$store.getters.currentUser.verified"
:callback="checkVerified"
Expand All @@ -34,6 +32,11 @@ export default {
Foot,
Interval
},
computed: {
customLayout: function () {
return this.$route.meta.customLayout
}
},
methods: {
checkVerified () {
this.$api
Expand All @@ -51,4 +54,8 @@ export default {
.full-height-content {
height: 100%
}
.tall-footer.footer {
height: 100%;
}
</style>
11 changes: 11 additions & 0 deletions src/backend/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,14 @@ export const updateLogo = async ({ file, fileName, wikiId }) => {
export const updateSetting = async (setting, payload) => axios.post(`/wiki/setting/${setting}/update`, { ...payload, setting })
export const updateSkin = async payload => updateSetting('wgDefaultSkin', payload)
export const wikiDetails = async payload => (await axios.post('/wiki/details', payload)).data.data
export const wikiDiscovery = async ({ sort, direction, active, currentPage, resultsPerPage }) => {
return (await axios.get('/wiki', {
params: {
sort: sort,
direction: direction,
is_active: active,
page: currentPage,
per_page: resultsPerPage
}
})).data
}
80 changes: 80 additions & 0 deletions src/backend/mocks/default_handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,83 @@ const removeWiki = wikiIndex => {
localStorage.setItem('msw-myWikis', JSON.stringify(myWikis))
}

const wikiDiscovery = (referrer, params) => {
const pseudorandom = {
seed: 1,
next: function () {
const x = Math.sin(this.seed++) * 10000
return x - Math.floor(x)
}
}

const names = [
'Wikibase Name',
'A Very Long Wikibase Name'
]

let wikis = [...Array(75).keys()].map((id) => {
const wiki = {
id: id,
domain: id + '-wikibase.wbaas.localhost',
sitename: id + ' - ' + names[id % names.length],
wiki_site_stats: null,
logo_url: null
}

if (pseudorandom.next() >= 0.1) {
wiki.wiki_site_stats = {
pages: Math.ceil(pseudorandom.next() * 250)
}
}

if (pseudorandom.next() >= 0.5) {
wiki.logo_url = new URL(referrer).origin + '/favicon.ico'
}
return wiki
})

if (parseInt(params.get('is_active'))) {
wikis = wikis.filter((wiki) => {
const stats = wiki.wiki_site_stats
return stats && stats.pages > 1
})
}

if (params.get('sort') === 'sitename') {
wikis = wikis.sort((a, b) => {
let sort = a.sitename.localeCompare(b.sitename, 'en', { numeric: true })
if (params.get('direction') === 'desc') {
sort *= -1
}
return sort
})
}

if (params.get('sort') === 'pages') {
wikis = wikis.sort((a, b) => {
const aPages = a.wiki_site_stats ? a.wiki_site_stats.pages : 0
const bPages = b.wiki_site_stats ? b.wiki_site_stats.pages : 0
if (params.get('direction') === 'desc') {
return bPages - aPages
}
return aPages - bPages
})
}

const currentPage = parseInt(params.get('page'))
const resultsPerPage = parseInt(params.get('per_page'))
const start = (currentPage - 1) * resultsPerPage
const end = start + resultsPerPage

return {
data: wikis.slice(start, end),
meta: {
last_page: Math.ceil(wikis.length / resultsPerPage),
total: wikis.length
}
}
}

export const handlers = [
/* User endpoints */
rest.post(/\/auth\/login$/, (req, res, ctx) => {
Expand Down Expand Up @@ -108,5 +185,8 @@ export const handlers = [
return res(ctx.status(404))
}
return res(ctx.json({ data: wikiDetails }), ctx.status(200))
}),
rest.get(/\/wiki$/, (req, res, ctx) => {
return res(ctx.json(wikiDiscovery(req.referrer, req.url.searchParams)))
})
]
10 changes: 8 additions & 2 deletions src/components/Layout/Foot.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<template>
<footer>
<footer class="footer">
<v-footer color="primary lighten-1">
<v-container>
<v-row dense>
<v-col cols="1"></v-col>
<v-col cols="10">
<ul class="footer-list">
<li><a target="_blank" rel="noopener noreferrer" href="https://wikiba.se/about-us/">About</a></li>
<li><router-link to="/discovery">Discovery</router-link></li>
<li><a target="_blank" rel="noopener noreferrer" href="https://www.mediawiki.org/wiki/Wikibase/Wikibase.cloud">Documentation</a></li>
<li><a target="_blank" rel="noopener noreferrer" href="https://github.com/wbstack">Github</a></li>
<li><router-link to="/privacy-policy">Privacy Policy</router-link></li>
Expand Down Expand Up @@ -39,5 +40,10 @@ export default {
.white-footer-links a{
color: white !important;
}
.footer {
height: auto;
}
.v-footer, .container {
height: 100%;
}
</style>
3 changes: 3 additions & 0 deletions src/components/Layout/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ export default {
</script>

<style scoped>
.v-toolbar {
max-height: 64px;
}
.no-button-pointer-events{
pointer-events: none
}
Expand Down
74 changes: 74 additions & 0 deletions src/components/Pages/Discovery/Components/DiscoveryCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<template>
<v-card @click="goToWiki" @keyup.enter="goToWiki">
<v-container class="content">
<v-row no-gutters>
<v-col class="flex-grow-0">
<v-avatar rounded :color="color">
<img v-if="logo" :src="logo.href">
<span v-else class="white--text">{{character}}</span>
</v-avatar>
</v-col>
<v-col class="details">
<div class="text-h5 font-weight-regular">{{name}}</div>
<div v-if="stats" class="text-body-2 pages">No. of pages: {{pages}}</div>
<div v-else class="text-body-2 pages">No. of pages: <i>currently unavailable</i></div>
</v-col>
</v-row>
</v-container>
</v-card>
</template>

<script>
export default {
name: 'DiscoveryCard',
props: {
name: {
type: String,
required: true
},
url: {
type: URL,
required: true
},
logo: {
type: URL,
required: false
},
stats: {
type: Boolean,
required: true
},
pages: {
type: Number,
required: false
}
},
computed: {
character: function () {
return this.name.substring(0, 1).toUpperCase()
},
color: function () {
const colors = ['red', 'blue', 'green', 'purple']
return this.logo ? 'white' : colors[Math.floor(Math.random() * colors.length)]
}
},
methods: {
goToWiki () {
window.open(this.url.href, '_blank')
}
}
}
</script>

<style scoped>
.content {
padding: 16px 16px;
}
.details.col {
padding-left: 16px !important;
word-wrap: anywhere;
}
.pages {
padding-top: 4px;
}
</style>
60 changes: 60 additions & 0 deletions src/components/Pages/Discovery/Components/MasonryGrid.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<template>
<div class="grid" ref="grid">
<slot/>
</div>
</template>

<script>
export default {
name: 'MasonryGrid',
data () {
return {
timer: null
}
},
methods: {
resizeCards () {
const cards = this.$refs.grid.children
const style = window.getComputedStyle(this.$refs.grid)
const rowGap = parseInt(style.getPropertyValue('row-gap'))
const gridAutoRows = parseInt(style.getPropertyValue('grid-auto-rows'))
const rowHeight = rowGap + gridAutoRows
Array.from(cards).forEach((card) => {
const cardHeight = card.firstChild.getBoundingClientRect().height + rowGap
const numRows = Math.round(cardHeight / rowHeight)
card.style['grid-row'] = 'span ' + numRows
})
},
debounceResize (time) {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.resizeCards()
}, time)
},
windowResized () {
this.debounceResize(50)
}
},
created () {
window.addEventListener('resize', this.windowResized)
},
destroyed () {
window.removeEventListener('resize', this.windowResized)
},
updated () {
this.$nextTick(() => {
this.resizeCards()
})
}
}
</script>

<style scoped>
.grid {
display: grid;
row-gap: 16px;
grid-template-columns: repeat(auto-fill, minmax(288px, 1fr));
grid-auto-rows: 1px;
}
</style>
Loading

0 comments on commit 6dcae41

Please sign in to comment.