parent
							
								
									df6b75e994
								
							
						
					
					
						commit
						58d1f62b2b
					
				
					 8 changed files with 140 additions and 4 deletions
				
			
		
							
								
								
									
										11
									
								
								src/routes/_actions/showShareDialogIfNecessary.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/routes/_actions/showShareDialogIfNecessary.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
import { store } from '../_store/store'
 | 
			
		||||
import { importShowComposeDialog } from '../_components/dialog/asyncDialogs'
 | 
			
		||||
 | 
			
		||||
export async function showShareDialogIfNecessary () {
 | 
			
		||||
  let { isUserLoggedIn, openShareDialog } = store.get()
 | 
			
		||||
  store.set({ openShareDialog: false })
 | 
			
		||||
  if (isUserLoggedIn && openShareDialog) {
 | 
			
		||||
    let showComposeDialog = await importShowComposeDialog()
 | 
			
		||||
    showComposeDialog()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -7,9 +7,28 @@
 | 
			
		|||
  import NotLoggedInHome from '../_components/NotLoggedInHome.html'
 | 
			
		||||
  import { store } from '../_store/store.js'
 | 
			
		||||
  import TimelineHomePage from '../_components/TimelineHomePage.html'
 | 
			
		||||
  import { observe } from 'svelte-extras'
 | 
			
		||||
  import { showShareDialogIfNecessary } from '../_actions/showShareDialogIfNecessary'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    async oncreate () {
 | 
			
		||||
      let observed = false
 | 
			
		||||
      this.observe('currentVerifyCredentials', verifyCredentials => {
 | 
			
		||||
        if (verifyCredentials && !observed) {
 | 
			
		||||
          // when the verifyCredentials object is available, we can check to see
 | 
			
		||||
          // if the user is trying to share something, then share it
 | 
			
		||||
          observed = true
 | 
			
		||||
          /* no await */ showShareDialogIfNecessary()
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    store: () => store,
 | 
			
		||||
    computed: {
 | 
			
		||||
      currentVerifyCredentials: ({ $currentVerifyCredentials }) => $currentVerifyCredentials
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
      observe
 | 
			
		||||
    },
 | 
			
		||||
    components: {
 | 
			
		||||
      NotLoggedInHome,
 | 
			
		||||
      TimelineHomePage
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -44,13 +44,15 @@ async function doRefreshInstanceDataAndStream (store, instanceName) {
 | 
			
		|||
 | 
			
		||||
async function refreshInstanceData (instanceName) {
 | 
			
		||||
  // these are all low-priority
 | 
			
		||||
  scheduleIdleTask(() => updateVerifyCredentialsForInstance(instanceName))
 | 
			
		||||
  scheduleIdleTask(() => updateCustomEmojiForInstance(instanceName))
 | 
			
		||||
  scheduleIdleTask(() => updateListsForInstance(instanceName))
 | 
			
		||||
  scheduleIdleTask(() => updatePushSubscriptionForInstance(instanceName))
 | 
			
		||||
 | 
			
		||||
  // this is the only critical one
 | 
			
		||||
  await updateInstanceInfo(instanceName)
 | 
			
		||||
  // these are the only critical ones
 | 
			
		||||
  await Promise.all([
 | 
			
		||||
    updateInstanceInfo(instanceName),
 | 
			
		||||
    updateVerifyCredentialsForInstance(instanceName)
 | 
			
		||||
  ])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function stream (store, instanceName, currentInstanceInfo) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,7 +46,8 @@ const nonPersistedState = {
 | 
			
		|||
  sensitivesShown: {},
 | 
			
		||||
  spoilersShown: {},
 | 
			
		||||
  statusModifications: {},
 | 
			
		||||
  verifyCredentials: {}
 | 
			
		||||
  verifyCredentials: {},
 | 
			
		||||
  openShareDialog: false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const state = Object.assign({}, persistedState, nonPersistedState)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										7
									
								
								src/routes/_utils/decodeURIComponentWithPluses.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/routes/_utils/decodeURIComponentWithPluses.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
// Per the web share target spec: https://wicg.github.io/web-share-target/
 | 
			
		||||
// U+0020 (SPACE) characters are encoded as "+", due to the use of
 | 
			
		||||
// application/x-www-form-urlencoded encoding, not "%20" as might be expected.
 | 
			
		||||
 | 
			
		||||
export function decodeURIComponentWithPluses (text) {
 | 
			
		||||
  return text.split('+').map(decodeURIComponent).join(' ')
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								src/routes/share.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/routes/share.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
<!-- this is just used for the web share target API -->
 | 
			
		||||
<script>
 | 
			
		||||
  import { store } from './_store/store'
 | 
			
		||||
  import { goto } from '../../__sapper__/client'
 | 
			
		||||
  import { decodeURIComponentWithPluses } from './_utils/decodeURIComponentWithPluses'
 | 
			
		||||
 | 
			
		||||
  const SHARE_KEYS = ['title', 'text', 'url']
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    store: () => store,
 | 
			
		||||
    oncreate () {
 | 
			
		||||
      let params = new URLSearchParams(location.search)
 | 
			
		||||
 | 
			
		||||
      let text = SHARE_KEYS
 | 
			
		||||
        .map(key => params.get(key) && decodeURIComponentWithPluses(params.get(key)))
 | 
			
		||||
        .filter(Boolean)
 | 
			
		||||
        .join(' ')
 | 
			
		||||
 | 
			
		||||
      this.store.set({ openShareDialog: true })
 | 
			
		||||
      this.store.clearComposeData('dialog')
 | 
			
		||||
      this.store.setComposeData('dialog', { text })
 | 
			
		||||
      this.store.save()
 | 
			
		||||
 | 
			
		||||
      goto('/', { replaceState: true })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -6,6 +6,17 @@
 | 
			
		|||
  "description": "Alternative web client for Mastodon, focused on speed and simplicity.",
 | 
			
		||||
  "display": "standalone",
 | 
			
		||||
  "start_url": "/?pwa=true",
 | 
			
		||||
  "share_target": {
 | 
			
		||||
    "action": "/share",
 | 
			
		||||
    "method": "GET",
 | 
			
		||||
    "enctype": "application/x-www-form-urlencoded",
 | 
			
		||||
    "url_template": "/share?title={title}&text={text}&url={url}",
 | 
			
		||||
    "params": {
 | 
			
		||||
      "title": "title",
 | 
			
		||||
      "text": "text",
 | 
			
		||||
      "url": "url"
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "icons": [
 | 
			
		||||
    {
 | 
			
		||||
      "src": "icon-48.png",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										58
									
								
								tests/spec/027-share-target.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								tests/spec/027-share-target.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
import {
 | 
			
		||||
  closeDialogButton,
 | 
			
		||||
  composeModalInput,
 | 
			
		||||
  getUrl, goBack, modalDialog, notificationsNavButton
 | 
			
		||||
} from '../utils'
 | 
			
		||||
import { loginAsFoobar } from '../roles'
 | 
			
		||||
 | 
			
		||||
fixture`027-share-target.js`
 | 
			
		||||
  .page`http://localhost:4002`
 | 
			
		||||
 | 
			
		||||
const SHARE_URL = 'http://localhost:4002/share?' +
 | 
			
		||||
  'title=My+cool+title&' +
 | 
			
		||||
  'text=This+is+a+bit+clich%C3%A9&' +
 | 
			
		||||
  'url=http%3A%2F%2Fexample.com'
 | 
			
		||||
 | 
			
		||||
const SHARE_TEXT = 'My cool title This is a bit cliché http://example.com'
 | 
			
		||||
 | 
			
		||||
test('Share target works when page is not open', async t => {
 | 
			
		||||
  await loginAsFoobar(t)
 | 
			
		||||
  await t
 | 
			
		||||
    .navigateTo('about:blank')
 | 
			
		||||
    .navigateTo(SHARE_URL)
 | 
			
		||||
    .expect(getUrl()).eql('http://localhost:4002/')
 | 
			
		||||
    .expect(modalDialog.hasAttribute('aria-hidden')).notOk()
 | 
			
		||||
    .expect(composeModalInput.value).eql(SHARE_TEXT)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test('Share target works when page is open', async t => {
 | 
			
		||||
  await loginAsFoobar(t)
 | 
			
		||||
  await t
 | 
			
		||||
    .navigateTo(SHARE_URL)
 | 
			
		||||
    .expect(getUrl()).eql('http://localhost:4002/')
 | 
			
		||||
    .expect(modalDialog.hasAttribute('aria-hidden')).notOk()
 | 
			
		||||
    .expect(composeModalInput.value).eql(SHARE_TEXT)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test('Share target page replaces itself in back nav history', async t => {
 | 
			
		||||
  await loginAsFoobar(t)
 | 
			
		||||
  await t
 | 
			
		||||
    .click(notificationsNavButton)
 | 
			
		||||
    .expect(getUrl()).contains('/notifications')
 | 
			
		||||
    .navigateTo(SHARE_URL)
 | 
			
		||||
    .expect(getUrl()).eql('http://localhost:4002/')
 | 
			
		||||
    .expect(modalDialog.hasAttribute('aria-hidden')).notOk()
 | 
			
		||||
    .expect(composeModalInput.value).eql(SHARE_TEXT)
 | 
			
		||||
    .click(closeDialogButton)
 | 
			
		||||
    .expect(modalDialog.exists).notOk()
 | 
			
		||||
  await goBack()
 | 
			
		||||
  await t
 | 
			
		||||
    .expect(getUrl()).contains('/notifications')
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
test('Share target does nothing when not logged in', async t => {
 | 
			
		||||
  await t
 | 
			
		||||
    .navigateTo(SHARE_URL)
 | 
			
		||||
    .expect(getUrl()).eql('http://localhost:4002/')
 | 
			
		||||
    .expect(modalDialog.exists).notOk()
 | 
			
		||||
})
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue