forked from cybrespace/pinafore
		
	implement autoplay gif feature for avatars
This commit is contained in:
		
							parent
							
								
									545d2a9d92
								
							
						
					
					
						commit
						f1eaee4674
					
				
					 9 changed files with 142 additions and 20 deletions
				
			
		| 
						 | 
				
			
			@ -1,9 +1,10 @@
 | 
			
		|||
<div class="account-profile {{headerIsMissing ? 'header-is-missing' : ''}}" style="background-image: url({{profile.header}});">
 | 
			
		||||
<div class="account-profile {{headerIsMissing ? 'header-is-missing' : ''}}"
 | 
			
		||||
     style="background-image: url({{$autoplayGifs ? profile.header : profile.header_static}});">
 | 
			
		||||
  <div class="account-profile-grid-wrapper">
 | 
			
		||||
    <div class="account-profile-backdrop"></div>
 | 
			
		||||
    <div class="account-profile-grid">
 | 
			
		||||
      <div class="account-profile-avatar">
 | 
			
		||||
        <img src="{{profile.avatar}}" aria-hidden="true">
 | 
			
		||||
        <Avatar account="{{profile}}" size="big" />
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="account-profile-name">
 | 
			
		||||
        <ExternalLink href="{{profile.url}}">
 | 
			
		||||
| 
						 | 
				
			
			@ -101,11 +102,6 @@
 | 
			
		|||
  .account-profile-avatar {
 | 
			
		||||
    grid-area: avatar;
 | 
			
		||||
  }
 | 
			
		||||
  .account-profile-avatar img {
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    width: 100px;
 | 
			
		||||
    height: 100px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .account-profile-username {
 | 
			
		||||
    grid-area: username;
 | 
			
		||||
| 
						 | 
				
			
			@ -159,10 +155,6 @@
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  @media (max-width: 767px) {
 | 
			
		||||
    .account-profile-avatar img {
 | 
			
		||||
      width: 80px;
 | 
			
		||||
      height: 80px;
 | 
			
		||||
    }
 | 
			
		||||
    .account-profile-name {
 | 
			
		||||
      font-size: 1.3em;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -190,14 +182,18 @@
 | 
			
		|||
<script>
 | 
			
		||||
  import IconButton from './IconButton.html'
 | 
			
		||||
  import ExternalLink from './ExternalLink.html'
 | 
			
		||||
  import Avatar from './Status/Avatar.html'
 | 
			
		||||
  import { store } from '../_store/store'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    computed: {
 | 
			
		||||
      headerIsMissing: (profile) => profile.header.endsWith('missing.png')
 | 
			
		||||
    },
 | 
			
		||||
    store: () => store,
 | 
			
		||||
    components: {
 | 
			
		||||
      IconButton,
 | 
			
		||||
      ExternalLink
 | 
			
		||||
      ExternalLink,
 | 
			
		||||
      Avatar
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										38
									
								
								routes/_components/NonAutoplayImg.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								routes/_components/NonAutoplayImg.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
{{#if staticSrc === src}}
 | 
			
		||||
  <img class="{{className || ''}}"
 | 
			
		||||
       aria-hidden="{{ariaHidden}}"
 | 
			
		||||
       alt="{{alt}}"
 | 
			
		||||
       src="{{src}}"
 | 
			
		||||
       on:imgLoadError />
 | 
			
		||||
{{else}}
 | 
			
		||||
  <img class="{{className || ''}} non-autoplay-zoom-in"
 | 
			
		||||
       aria-hidden="{{ariaHidden}}"
 | 
			
		||||
       alt="{{alt}}"
 | 
			
		||||
       src="{{staticSrc}}"
 | 
			
		||||
       on:imgLoadError
 | 
			
		||||
       on:mouseover="onMouseOver(event)"
 | 
			
		||||
       ref:node />
 | 
			
		||||
{{/if}}
 | 
			
		||||
<style>
 | 
			
		||||
  .non-autoplay-zoom-in {
 | 
			
		||||
    cursor: zoom-in;
 | 
			
		||||
  }
 | 
			
		||||
</style>
 | 
			
		||||
<script>
 | 
			
		||||
  import { imgLoadError, mouseover } from '../_utils/events'
 | 
			
		||||
  export default {
 | 
			
		||||
    methods: {
 | 
			
		||||
      onMouseOver(mouseOver) {
 | 
			
		||||
        if (mouseOver) {
 | 
			
		||||
          this.refs.node.src = this.get('src')
 | 
			
		||||
        } else {
 | 
			
		||||
          this.refs.node.src = this.get('staticSrc')
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    events: {
 | 
			
		||||
      imgLoadError,
 | 
			
		||||
      mouseover
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,16 +1,33 @@
 | 
			
		|||
{{#if error}}
 | 
			
		||||
<svg class="{{className}} avatar" aria-hidden="true">
 | 
			
		||||
<svg class="{{className}} avatar size-{{size}}" aria-hidden="true">
 | 
			
		||||
  <use xlink:href="#fa-user" />
 | 
			
		||||
</svg>
 | 
			
		||||
{{else}}
 | 
			
		||||
<img class="{{className}} avatar" aria-hidden="true" alt=""
 | 
			
		||||
{{elseif $autoplayGifs}}
 | 
			
		||||
  <img class="{{className}} avatar size-{{size}}" aria-hidden="true" alt=""
 | 
			
		||||
     src="{{account.avatar}}" on:imgLoadError="set({error: true})" />
 | 
			
		||||
{{else}}
 | 
			
		||||
  <NonAutoplayImg className="{{className}} avatar size-{{size}}" ariaHidden="true" alt=""
 | 
			
		||||
       src="{{account.avatar}}" staticSrc="{{account.avatar_static}}" on:imgLoadError="set({error: true})" />
 | 
			
		||||
{{/if}}
 | 
			
		||||
<style>
 | 
			
		||||
  .avatar {
 | 
			
		||||
  :global(.avatar) {
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
  }
 | 
			
		||||
  :global(.avatar.size-small) {
 | 
			
		||||
    width: 48px;
 | 
			
		||||
    height: 48px;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  :global(.avatar.size-big) {
 | 
			
		||||
    width: 100px;
 | 
			
		||||
    height: 100px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @media (max-width: 767px) {
 | 
			
		||||
    :global(.avatar.size-big) {
 | 
			
		||||
      width: 80px;
 | 
			
		||||
      height: 80px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  svg.avatar {
 | 
			
		||||
| 
						 | 
				
			
			@ -19,10 +36,16 @@
 | 
			
		|||
</style>
 | 
			
		||||
<script>
 | 
			
		||||
  import { imgLoadError } from '../../_utils/events'
 | 
			
		||||
  import { store } from '../../_store/store'
 | 
			
		||||
  import NonAutoplayImg from '../NonAutoplayImg.html'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    events: {
 | 
			
		||||
      imgLoadError
 | 
			
		||||
    },
 | 
			
		||||
    store: () => store,
 | 
			
		||||
    components: {
 | 
			
		||||
      NonAutoplayImg
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +30,7 @@
 | 
			
		|||
      </a>
 | 
			
		||||
    {{/if}}
 | 
			
		||||
  </div>
 | 
			
		||||
  <Avatar account={{originalAccount}} className="status-sidebar"/>
 | 
			
		||||
  <Avatar account={{originalAccount}} className="status-sidebar" size="small" />
 | 
			
		||||
  {{#if originalStatus.spoiler_text}}
 | 
			
		||||
    <div class="status-spoiler">{{originalStatus.spoiler_text}}</div>
 | 
			
		||||
  {{/if}}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,8 @@ const KEYS_TO_STORE_IN_LOCAL_STORAGE = new Set([
 | 
			
		|||
  "instanceNameInSearch",
 | 
			
		||||
  "instanceThemes",
 | 
			
		||||
  "loggedInInstances",
 | 
			
		||||
  "loggedInInstancesInOrder"
 | 
			
		||||
  "loggedInInstancesInOrder",
 | 
			
		||||
  "autoplayGifs"
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
class PinaforeStore extends LocalStorageStore {
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +25,8 @@ const store = new PinaforeStore({
 | 
			
		|||
  currentInstance: null,
 | 
			
		||||
  loggedInInstances: {},
 | 
			
		||||
  loggedInInstancesInOrder: [],
 | 
			
		||||
  instanceThemes: {}
 | 
			
		||||
  instanceThemes: {},
 | 
			
		||||
  autoplayGifs: false
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
mixins(PinaforeStore)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,3 +17,20 @@ export function imgLoad (node, callback) {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function mouseover(node, callback) {
 | 
			
		||||
  function onMouseEnter() {
 | 
			
		||||
    callback(true)
 | 
			
		||||
  }
 | 
			
		||||
  function onMouseLeave() {
 | 
			
		||||
    callback(false)
 | 
			
		||||
  }
 | 
			
		||||
  node.addEventListener('mouseenter', onMouseEnter)
 | 
			
		||||
  node.addEventListener('mouseleave', onMouseLeave)
 | 
			
		||||
  return {
 | 
			
		||||
    teardown () {
 | 
			
		||||
      node.removeEventListener('mouseenter', onMouseEnter)
 | 
			
		||||
      node.removeEventListener('mouseleave', onMouseLeave)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -43,6 +43,7 @@
 | 
			
		|||
  const NAV_ITEMS = {
 | 
			
		||||
    'settings': 'Settings',
 | 
			
		||||
    'settings/about': 'About Pinafore',
 | 
			
		||||
    'settings/general': 'General',
 | 
			
		||||
    'settings/instances': 'Instances',
 | 
			
		||||
    'settings/instances/add': 'Add an instance',
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										44
									
								
								routes/settings/general.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								routes/settings/general.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
<:Head>
 | 
			
		||||
  <title>General Settings</title>
 | 
			
		||||
</:Head>
 | 
			
		||||
 | 
			
		||||
<Layout page='settings'>
 | 
			
		||||
  <SettingsLayout page='settings/general' label="General">
 | 
			
		||||
    <h1>General Settings</h1>
 | 
			
		||||
 | 
			
		||||
    <h2>UI</h2>
 | 
			
		||||
    <form class="ui-settings" aria-label="UI settings">
 | 
			
		||||
      <div class="setting-group">
 | 
			
		||||
        <input type="checkbox" id="choice-autoplay-gif"
 | 
			
		||||
               bind:checked="$autoplayGifs" on:change="store.save()">
 | 
			
		||||
        <label for="choice-autoplay-gif">Autoplay GIFs</label>
 | 
			
		||||
      </div>
 | 
			
		||||
    </form>
 | 
			
		||||
 | 
			
		||||
  </SettingsLayout>
 | 
			
		||||
</Layout>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
  .ui-settings {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    background: var(--form-bg);
 | 
			
		||||
    border: 1px solid var(--main-border);
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    padding: 20px;
 | 
			
		||||
    line-height: 2em;
 | 
			
		||||
  }
 | 
			
		||||
</style>
 | 
			
		||||
<script>
 | 
			
		||||
  import Layout from '../_components/Layout.html';
 | 
			
		||||
  import SettingsLayout from './_components/SettingsLayout.html'
 | 
			
		||||
  import { store } from '../_store/store'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    components: {
 | 
			
		||||
      Layout,
 | 
			
		||||
      SettingsLayout
 | 
			
		||||
    },
 | 
			
		||||
    store: () => store
 | 
			
		||||
  };
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -7,6 +7,7 @@
 | 
			
		|||
    <h1>Settings</h1>
 | 
			
		||||
 | 
			
		||||
    <SettingsList>
 | 
			
		||||
      <SettingsListItem href="/settings/general" label="General"/>
 | 
			
		||||
      <SettingsListItem href="/settings/instances" label="Instances"/>
 | 
			
		||||
      <SettingsListItem href="/settings/about" label="About Pinafore"/>
 | 
			
		||||
    </SettingsList>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue