forked from cybrespace/pinafore
		
	add favorites, refactor local/federated
This commit is contained in:
		
							parent
							
								
									fa401d727f
								
							
						
					
					
						commit
						016ecfca8d
					
				
					 14 changed files with 280 additions and 15 deletions
				
			
		|  | @ -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,15 +6,25 @@ | |||
| 		<li> | ||||
|       <NavItem :page name="notifications" href="/notifications" svg="#fa-bell" label="Notifications" /> | ||||
| 		</li> | ||||
| 		<li> | ||||
|       <NavItem :page name="local" href="/local" svg="#fa-users" label="Local" /> | ||||
| 		</li> | ||||
| 		<li> | ||||
|       <NavItem :page name="federated" href="/federated" svg="#fa-globe" label="Federated" /> | ||||
| 		</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="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" /> | ||||
| 		</li> | ||||
|  | @ -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' | ||||
|  |  | |||
							
								
								
									
										28
									
								
								routes/community/_components/PageList.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								routes/community/_components/PageList.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -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> | ||||
							
								
								
									
										85
									
								
								routes/community/_components/PageListItem.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								routes/community/_components/PageListItem.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -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> | ||||
							
								
								
									
										61
									
								
								routes/community/index.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								routes/community/index.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -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> | ||||
							
								
								
									
										38
									
								
								routes/favorites.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								routes/favorites.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -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…
	
	Add table
		
		Reference in a new issue