forked from cybrespace/pinafore
add favorites, refactor local/federated
This commit is contained in:
parent
fa401d727f
commit
016ecfca8d
|
@ -19,4 +19,7 @@ module.exports = [
|
|||
{id:'fa-user-plus', src:'node_modules/font-awesome-svg-png/white/svg/user-plus.svg', title: 'Follow'},
|
||||
{id:'fa-external-link', src:'node_modules/font-awesome-svg-png/white/svg/external-link.svg', title: 'External Link'},
|
||||
{id:'fa-search', src:'node_modules/font-awesome-svg-png/white/svg/search.svg', title: 'Search'},
|
||||
{id:'fa-comments', src:'node_modules/font-awesome-svg-png/white/svg/comments.svg', title: 'Conversations'},
|
||||
{id:'fa-paperclip', src:'node_modules/font-awesome-svg-png/white/svg/paperclip.svg', title: 'Paperclip'},
|
||||
{id:'fa-thumbtack', src:'node_modules/font-awesome-svg-png/white/svg/thumb-tack.svg', title: 'Thumbtack'},
|
||||
]
|
|
@ -1,4 +1,9 @@
|
|||
<div class="dynamic-page-banner">
|
||||
<div class="dynamic-page-banner {{icon ? 'dynamic-page-with-icon' : ''}}">
|
||||
{{#if icon}}
|
||||
<svg>
|
||||
<use xlink:href="{{icon}}" />
|
||||
</svg>
|
||||
{{/if}}
|
||||
<h1 class="dynamic-page-title">{{title}}</h1>
|
||||
<button type="button"
|
||||
class="dynamic-page-go-back"
|
||||
|
@ -6,10 +11,19 @@
|
|||
</div>
|
||||
<style>
|
||||
.dynamic-page-banner {
|
||||
display: flex;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 20px 20px 20px;
|
||||
grid-template-columns: 1fr min-content;
|
||||
grid-column-gap: 10px;
|
||||
}
|
||||
.dynamic-page-banner.dynamic-page-with-icon {
|
||||
grid-template-columns: min-content 1fr min-content;
|
||||
}
|
||||
.dynamic-page-banner svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
fill: var(--body-text-color);
|
||||
}
|
||||
h1.dynamic-page-title {
|
||||
margin: 0;
|
||||
|
@ -23,6 +37,7 @@
|
|||
border: 0;
|
||||
padding: 0;
|
||||
background: none;
|
||||
justify-self: flex-end;
|
||||
}
|
||||
button.dynamic-page-go-back:hover {
|
||||
text-decoration: underline;
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
<button type="button"
|
||||
aria-label="{{label}}"
|
||||
aria-pressed="{{!!pressed}}"
|
||||
class="icon-button {{pressed ? 'pressed' : ''}} {{big ? 'big-icon' : ''}}">
|
||||
class="icon-button {{pressed ? 'pressed' : ''}} {{big ? 'big-icon' : ''}}"
|
||||
on:click
|
||||
>
|
||||
<svg>
|
||||
<use xlink:href="{{href}}" />
|
||||
</svg>
|
||||
|
@ -10,7 +12,9 @@
|
|||
{{else}}
|
||||
<button type="button"
|
||||
aria-label="{{label}}"
|
||||
class="icon-button {{big ? 'big-icon' : ''}}">
|
||||
class="icon-button {{big ? 'big-icon' : ''}}"
|
||||
on:click
|
||||
>
|
||||
<svg>
|
||||
<use xlink:href="{{href}}" />
|
||||
</svg>
|
||||
|
|
|
@ -6,14 +6,24 @@
|
|||
<li>
|
||||
<NavItem :page name="notifications" href="/notifications" svg="#fa-bell" label="Notifications" />
|
||||
</li>
|
||||
<li>
|
||||
<NavItem :page name="search" href="/search" svg="#fa-search" label="Search" />
|
||||
</li>
|
||||
{{#if $pinnedPage === '/local'}}
|
||||
<li>
|
||||
<NavItem :page name="local" href="/local" svg="#fa-users" label="Local" />
|
||||
</li>
|
||||
{{elseif $pinnedPage === '/federated'}}
|
||||
<li>
|
||||
<NavItem :page name="federated" href="/federated" svg="#fa-globe" label="Federated" />
|
||||
</li>
|
||||
{{elseif $pinnedPage === '/favorites'}}
|
||||
<li>
|
||||
<NavItem :page name="search" href="/search" svg="#fa-search" label="Search" />
|
||||
<NavItem :page name="favorites" href="/favorites" svg="#fa-star" label="Favorites" />
|
||||
</li>
|
||||
{{/if}}
|
||||
<li>
|
||||
<NavItem :page name="community" href="/community" svg="#fa-comments" label="Community" />
|
||||
</li>
|
||||
<li>
|
||||
<NavItem :page name="settings" href="/settings" svg="#fa-gear" label="Settings" />
|
||||
|
@ -49,7 +59,10 @@
|
|||
</style>
|
||||
<script>
|
||||
import NavItem from './NavItem'
|
||||
import { store } from '../_store/store'
|
||||
|
||||
export default {
|
||||
store: () => store,
|
||||
components: {
|
||||
NavItem
|
||||
}
|
||||
|
|
|
@ -46,4 +46,9 @@ export function instanceComputations(store) {
|
|||
['currentInstance', 'verifyCredentials'],
|
||||
(currentInstance, verifyCredentials) => verifyCredentials && verifyCredentials[currentInstance]
|
||||
)
|
||||
|
||||
store.compute(
|
||||
'pinnedPage',
|
||||
['pinnedPages', 'currentInstance'],
|
||||
(pinnedPages, currentInstance) => (currentInstance && pinnedPages[currentInstance]) || '/local')
|
||||
}
|
|
@ -12,7 +12,8 @@ const KEYS_TO_STORE_IN_LOCAL_STORAGE = new Set([
|
|||
"loggedInInstances",
|
||||
"loggedInInstancesInOrder",
|
||||
"autoplayGifs",
|
||||
"markMediaAsSensitive"
|
||||
"markMediaAsSensitive",
|
||||
"pinnedPages"
|
||||
])
|
||||
|
||||
class PinaforeStore extends LocalStorageStore {
|
||||
|
@ -31,7 +32,8 @@ const store = new PinaforeStore({
|
|||
spoilersShown: {},
|
||||
sensitivesShown: {},
|
||||
autoplayGifs: false,
|
||||
markMediaAsSensitive: false
|
||||
markMediaAsSensitive: false,
|
||||
pinnedPages: {}
|
||||
})
|
||||
|
||||
mixins(PinaforeStore)
|
||||
|
|
|
@ -10,6 +10,8 @@ function getTimelineUrlPath(timeline) {
|
|||
return 'timelines/home'
|
||||
case 'notifications':
|
||||
return 'notifications'
|
||||
case 'favorites':
|
||||
return 'favourites'
|
||||
}
|
||||
if (timeline.startsWith('tag/')) {
|
||||
return 'timelines/tag'
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<div class="page-list-wrapper">
|
||||
{{#if label}}
|
||||
<ul class="page-list" aria-label="{{label}}">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
{{else}}
|
||||
<ul class="page-list">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
{{/if}}
|
||||
</div>
|
||||
<style>
|
||||
.page-list-wrapper {
|
||||
margin: 20px 20px;
|
||||
}
|
||||
ul.page-list {
|
||||
list-style: none;
|
||||
width: 100%;
|
||||
border: 1px solid var(--settings-list-item-border);
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.page-list-wrapper {
|
||||
margin: 20px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,85 @@
|
|||
<li class="page-list-item">
|
||||
<a :href>
|
||||
<svg>
|
||||
<use xlink:href="{{icon}}" />
|
||||
</svg>
|
||||
<span aria-label="{{label}} {{$pinnedPage === href ? 'Pinned page' : 'Unpinned page'}}">
|
||||
{{label}}
|
||||
</span>
|
||||
<IconButton pressable="true"
|
||||
pressed="{{$pinnedPage === href}}"
|
||||
label="Pin page"
|
||||
href="#fa-thumbtack"
|
||||
on:click="onPinClick(event)"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
<style>
|
||||
.page-list-item {
|
||||
border: 1px solid var(--settings-list-item-border);
|
||||
font-size: 1.3em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.page-list-item a {
|
||||
padding: 20px 40px;
|
||||
background: var(--settings-list-item-bg);
|
||||
display: grid;
|
||||
grid-template-columns: min-content 1fr min-content;
|
||||
align-items: center;
|
||||
}
|
||||
.page-list-item a, .page-list-item a:visited {
|
||||
color: var(--body-text-color);
|
||||
}
|
||||
.page-list-item a:hover {
|
||||
text-decoration: none;
|
||||
background: var(--settings-list-item-bg-hover);
|
||||
color: var(--body-text-color);
|
||||
}
|
||||
.page-list-item a:active {
|
||||
background: var(--settings-list-item-bg-active);
|
||||
}
|
||||
.page-list-item svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: inline-block;
|
||||
margin-right: 20px;
|
||||
fill: var(--body-text-color);
|
||||
}
|
||||
.page-list-item span {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.page-list-item a {
|
||||
padding: 20px 10px;
|
||||
}
|
||||
.page-list-item svg {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
import { store } from '../../_store/store'
|
||||
import IconButton from '../../_components/IconButton'
|
||||
|
||||
export default {
|
||||
store: () => store,
|
||||
components: {
|
||||
IconButton
|
||||
},
|
||||
methods: {
|
||||
onPinClick(e) {
|
||||
e.preventDefault()
|
||||
let currentInstance = this.store.get('currentInstance')
|
||||
let pinnedPages = this.store.get('pinnedPages')
|
||||
let href = this.get('href')
|
||||
pinnedPages[currentInstance] = href
|
||||
this.store.set({pinnedPages: pinnedPages})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,61 @@
|
|||
<:Head>
|
||||
<title>Pinafore – Community</title>
|
||||
</:Head>
|
||||
|
||||
<Layout page='community'>
|
||||
{{#if $isUserLoggedIn}}
|
||||
<div class="community-page">
|
||||
<PageList label="Community list">
|
||||
<PageListItem href="/local"
|
||||
label="Local Timeline"
|
||||
icon="#fa-users"
|
||||
/>
|
||||
<PageListItem href="/federated"
|
||||
label="Federated Timeline"
|
||||
icon="#fa-globe"
|
||||
/>
|
||||
<PageListItem href="/favorites"
|
||||
label="Favorites"
|
||||
icon="#fa-star"
|
||||
/>
|
||||
</PageList>
|
||||
</div>
|
||||
{{else}}
|
||||
<HiddenFromSSR>
|
||||
<FreeTextLayout>
|
||||
<h1>More</h1>
|
||||
|
||||
<p>More options appear here when logged in.</p>
|
||||
</FreeTextLayout>
|
||||
</HiddenFromSSR>
|
||||
{{/if}}
|
||||
</Layout>
|
||||
<style>
|
||||
.community-page {
|
||||
margin: 20px;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.community-page {
|
||||
margin: 20px 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import Layout from '../_components/Layout.html'
|
||||
import FreeTextLayout from '../_components/FreeTextLayout.html'
|
||||
import { store } from '../_store/store.js'
|
||||
import HiddenFromSSR from '../_components/HiddenFromSSR'
|
||||
import PageList from './_components/PageList.html'
|
||||
import PageListItem from './_components/PageListItem.html'
|
||||
|
||||
export default {
|
||||
store: () => store,
|
||||
components: {
|
||||
Layout,
|
||||
FreeTextLayout,
|
||||
HiddenFromSSR,
|
||||
PageList,
|
||||
PageListItem
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -0,0 +1,38 @@
|
|||
<:Head>
|
||||
<title>Pinafore – Favorites</title>
|
||||
</:Head>
|
||||
|
||||
<Layout page='favorites' virtual="true" virtualRealm="favorites">
|
||||
{{#if $isUserLoggedIn}}
|
||||
<DynamicPageBanner title="Favorites" icon="#fa-star"/>
|
||||
<LazyTimeline timeline='favorites' />
|
||||
{{else}}
|
||||
<HiddenFromSSR>
|
||||
<FreeTextLayout>
|
||||
<h1>Favorites</h1>
|
||||
|
||||
<p>Your favorites will appear here when logged in.</p>
|
||||
</FreeTextLayout>
|
||||
</HiddenFromSSR>
|
||||
{{/if}}
|
||||
</Layout>
|
||||
|
||||
<script>
|
||||
import Layout from './_components/Layout.html'
|
||||
import LazyTimeline from './_components/timeline/LazyTimeline.html'
|
||||
import FreeTextLayout from './_components/FreeTextLayout.html'
|
||||
import { store } from './_store/store.js'
|
||||
import HiddenFromSSR from './_components/HiddenFromSSR'
|
||||
import DynamicPageBanner from './_components/DynamicPageBanner.html'
|
||||
|
||||
export default {
|
||||
store: () => store,
|
||||
components: {
|
||||
Layout,
|
||||
LazyTimeline,
|
||||
FreeTextLayout,
|
||||
HiddenFromSSR,
|
||||
DynamicPageBanner
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
<Layout page='federated' virtual="true" virtualRealm="federated">
|
||||
{{#if $isUserLoggedIn}}
|
||||
<DynamicPageBanner title="Federated Timeline" icon="#fa-globe"/>
|
||||
<LazyTimeline timeline='federated' />
|
||||
{{else}}
|
||||
<HiddenFromSSR>
|
||||
|
@ -22,6 +23,7 @@
|
|||
import FreeTextLayout from './_components/FreeTextLayout.html'
|
||||
import { store } from './_store/store.js'
|
||||
import HiddenFromSSR from './_components/HiddenFromSSR'
|
||||
import DynamicPageBanner from './_components/DynamicPageBanner.html'
|
||||
|
||||
export default {
|
||||
store: () => store,
|
||||
|
@ -29,7 +31,8 @@
|
|||
Layout,
|
||||
LazyTimeline,
|
||||
FreeTextLayout,
|
||||
HiddenFromSSR
|
||||
HiddenFromSSR,
|
||||
DynamicPageBanner
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
<Layout page='local' virtual="true" virtualRealm="local">
|
||||
{{#if $isUserLoggedIn}}
|
||||
<DynamicPageBanner title="Local Timeline" icon="#fa-users"/>
|
||||
<LazyTimeline timeline='local' />
|
||||
{{else}}
|
||||
<HiddenFromSSR>
|
||||
|
@ -22,6 +23,7 @@
|
|||
import FreeTextLayout from './_components/FreeTextLayout.html'
|
||||
import { store } from './_store/store.js'
|
||||
import HiddenFromSSR from './_components/HiddenFromSSR'
|
||||
import DynamicPageBanner from './_components/DynamicPageBanner.html'
|
||||
|
||||
export default {
|
||||
store: () => store,
|
||||
|
@ -29,7 +31,8 @@
|
|||
Layout,
|
||||
LazyTimeline,
|
||||
FreeTextLayout,
|
||||
HiddenFromSSR
|
||||
HiddenFromSSR,
|
||||
DynamicPageBanner
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -84,6 +84,9 @@ body.offline,body.theme-hotpants.offline,body.theme-majesty.offline,body.theme-o
|
|||
<symbol id="fa-user-plus" viewBox="0 0 2048 1792"><title>Follow</title><path d="M704 896q-159 0-271.5-112.5T320 512t112.5-271.5T704 128t271.5 112.5T1088 512 975.5 783.5 704 896zm960 128h352q13 0 22.5 9.5t9.5 22.5v192q0 13-9.5 22.5t-22.5 9.5h-352v352q0 13-9.5 22.5t-22.5 9.5h-192q-13 0-22.5-9.5t-9.5-22.5v-352h-352q-13 0-22.5-9.5t-9.5-22.5v-192q0-13 9.5-22.5t22.5-9.5h352V672q0-13 9.5-22.5t22.5-9.5h192q13 0 22.5 9.5t9.5 22.5v352zm-736 224q0 52 38 90t90 38h256v238q-68 50-171 50H267q-121 0-194-69T0 1405q0-53 3.5-103.5t14-109T44 1084t43-97.5 62-81 85.5-53.5T346 832q19 0 39 17 79 61 154.5 91.5T704 971t164.5-30.5T1023 849q20-17 39-17 132 0 217 96h-223q-52 0-90 38t-38 90v192z"></path></symbol>
|
||||
<symbol id="fa-external-link" viewBox="0 0 1792 1792"><title>External Link</title><path d="M1408 928v320q0 119-84.5 203.5T1120 1536H288q-119 0-203.5-84.5T0 1248V416q0-119 84.5-203.5T288 128h704q14 0 23 9t9 23v64q0 14-9 23t-23 9H288q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113V928q0-14 9-23t23-9h64q14 0 23 9t9 23zm384-864v512q0 26-19 45t-45 19-45-19l-176-176-652 652q-10 10-23 10t-23-10L695 983q-10-10-10-23t10-23l652-652-176-176q-19-19-19-45t19-45 45-19h512q26 0 45 19t19 45z"></path></symbol>
|
||||
<symbol id="fa-search" viewBox="0 0 1792 1792"><title>Search</title><path d="M1216 832q0-185-131.5-316.5T768 384 451.5 515.5 320 832t131.5 316.5T768 1280t316.5-131.5T1216 832zm512 832q0 52-38 90t-90 38q-54 0-90-38l-343-342q-179 124-399 124-143 0-273.5-55.5t-225-150-150-225T64 832t55.5-273.5 150-225 225-150T768 128t273.5 55.5 225 150 150 225T1472 832q0 220-124 399l343 343q37 37 37 90z"></path></symbol>
|
||||
<symbol id="fa-comments" viewBox="0 0 1792 1792"><title>Conversations</title><path d="M1408 768q0 139-94 257t-256.5 186.5T704 1280q-86 0-176-16-124 88-278 128-36 9-86 16h-3q-11 0-20.5-8t-11.5-21q-1-3-1-6.5t.5-6.5 2-6l2.5-5 3.5-5.5 4-5 4.5-5 4-4.5q5-6 23-25t26-29.5 22.5-29 25-38.5 20.5-44q-124-72-195-177T0 768q0-139 94-257t256.5-186.5T704 256t353.5 68.5T1314 511t94 257zm384 256q0 120-71 224.5T1526 1425q10 24 20.5 44t25 38.5 22.5 29 26 29.5 23 25q1 1 4 4.5t4.5 5 4 5 3.5 5.5l2.5 5 2 6 .5 6.5-1 6.5q-3 14-13 22t-22 7q-50-7-86-16-154-40-278-128-90 16-176 16-271 0-472-132 58 4 88 4 161 0 309-45t264-129q125-92 192-212t67-254q0-77-23-152 129 71 204 178t75 230z"></path></symbol>
|
||||
<symbol id="fa-paperclip" viewBox="0 0 1792 1792"><title>Paperclip</title><path d="M1596 1385q0 117-79 196t-196 79q-135 0-235-100L309 784Q196 669 196 513q0-159 110-270t269-111q158 0 273 113l605 606q10 10 10 22 0 16-30.5 46.5T1386 950q-13 0-23-10L757 333q-79-77-181-77-106 0-179 75t-73 181q0 105 76 181l776 777q63 63 145 63 64 0 106-42t42-106q0-82-63-145L825 659q-26-24-60-24-29 0-48 19t-19 48q0 32 25 59l410 410q10 10 10 22 0 16-31 47t-47 31q-12 0-22-10L633 851q-63-61-63-149 0-82 57-139t139-57q88 0 149 63l581 581q100 98 100 235z"></path></symbol>
|
||||
<symbol id="fa-thumbtack" viewBox="0 0 1792 1792"><title>Thumbtack</title><path d="M800 864V416q0-14-9-23t-23-9-23 9-9 23v448q0 14 9 23t23 9 23-9 9-23zm672 352q0 26-19 45t-45 19H979l-51 483q-2 12-10.5 20.5T897 1792h-1q-27 0-32-27l-76-485H384q-26 0-45-19t-19-45q0-123 78.5-221.5T576 896V384q-52 0-90-38t-38-90 38-90 90-38h640q52 0 90 38t38 90-38 90-90 38v512q99 0 177.5 98.5T1472 1216z"></path></symbol>
|
||||
</svg><!-- end insert svg here -->
|
||||
</svg>
|
||||
<!-- The application will be rendered inside this element,
|
||||
|
|
Loading…
Reference in New Issue