diff --git a/src/routes/_actions/showShareDialogIfNecessary.js b/src/routes/_actions/showShareDialogIfNecessary.js new file mode 100644 index 0000000..c720744 --- /dev/null +++ b/src/routes/_actions/showShareDialogIfNecessary.js @@ -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() + } +} diff --git a/src/routes/_pages/index.html b/src/routes/_pages/index.html index 60d91e9..ab73c22 100644 --- a/src/routes/_pages/index.html +++ b/src/routes/_pages/index.html @@ -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 diff --git a/src/routes/_store/observers/instanceObservers.js b/src/routes/_store/observers/instanceObservers.js index 6399cee..aeebc3f 100644 --- a/src/routes/_store/observers/instanceObservers.js +++ b/src/routes/_store/observers/instanceObservers.js @@ -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) { diff --git a/src/routes/_store/store.js b/src/routes/_store/store.js index d02c290..1d8560c 100644 --- a/src/routes/_store/store.js +++ b/src/routes/_store/store.js @@ -46,7 +46,8 @@ const nonPersistedState = { sensitivesShown: {}, spoilersShown: {}, statusModifications: {}, - verifyCredentials: {} + verifyCredentials: {}, + openShareDialog: false } const state = Object.assign({}, persistedState, nonPersistedState) diff --git a/src/routes/_utils/decodeURIComponentWithPluses.js b/src/routes/_utils/decodeURIComponentWithPluses.js new file mode 100644 index 0000000..e9131d4 --- /dev/null +++ b/src/routes/_utils/decodeURIComponentWithPluses.js @@ -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(' ') +} diff --git a/src/routes/share.html b/src/routes/share.html new file mode 100644 index 0000000..d99c1d0 --- /dev/null +++ b/src/routes/share.html @@ -0,0 +1,27 @@ + + diff --git a/static/manifest.json b/static/manifest.json index 356e501..73cf7d4 100644 --- a/static/manifest.json +++ b/static/manifest.json @@ -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", diff --git a/tests/spec/027-share-target.js b/tests/spec/027-share-target.js new file mode 100644 index 0000000..cefbd82 --- /dev/null +++ b/tests/spec/027-share-target.js @@ -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() +})