kopia lustrzana https://github.com/nolanlawson/pinafore
				
				
				
			fix: fix bell notifications, add tests
							rodzic
							
								
									2e9afd711f
								
							
						
					
					
						commit
						c67be9acc2
					
				| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
export default [
 | 
			
		||||
  { id: 'pinafore-logo', src: 'src/static/sailboat.svg', inline: true },
 | 
			
		||||
  { id: 'fa-bell', src: 'src/thirdparty/font-awesome-svg-png/white/svg/bell.svg', inline: true },
 | 
			
		||||
  { id: 'fa-bell-o', src: 'src/thirdparty/font-awesome-svg-png/white/svg/bell-o.svg' },
 | 
			
		||||
  { id: 'fa-users', src: 'src/thirdparty/font-awesome-svg-png/white/svg/users.svg', inline: true },
 | 
			
		||||
  { id: 'fa-globe', src: 'src/thirdparty/font-awesome-svg-png/white/svg/globe.svg' },
 | 
			
		||||
  { id: 'fa-gear', src: 'src/thirdparty/font-awesome-svg-png/white/svg/gear.svg', inline: true },
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +23,7 @@ export default [
 | 
			
		|||
  { id: 'fa-user-plus', src: 'src/thirdparty/font-awesome-svg-png/white/svg/user-plus.svg' },
 | 
			
		||||
  { id: 'fa-external-link', src: 'src/thirdparty/font-awesome-svg-png/white/svg/external-link.svg' },
 | 
			
		||||
  { id: 'fa-search', src: 'src/thirdparty/font-awesome-svg-png/white/svg/search.svg', inline: true },
 | 
			
		||||
  { id: 'fa-comment', src: 'src/thirdparty/font-awesome-svg-png/white/svg/comment.svg' },
 | 
			
		||||
  { id: 'fa-comments', src: 'src/thirdparty/font-awesome-svg-png/white/svg/comments.svg', inline: true },
 | 
			
		||||
  { id: 'fa-paperclip', src: 'src/thirdparty/font-awesome-svg-png/white/svg/paperclip.svg' },
 | 
			
		||||
  { id: 'fa-thumb-tack', src: 'src/thirdparty/font-awesome-svg-png/white/svg/thumb-tack.svg' },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -309,10 +309,10 @@ export default {
 | 
			
		|||
    true {(follow requested)}
 | 
			
		||||
    other {}
 | 
			
		||||
  }`,
 | 
			
		||||
  notifyLabel: `Subscribe`,
 | 
			
		||||
  denotifyLabel: `Unsubscribe`,
 | 
			
		||||
  notify: 'Subscribe to {account}',
 | 
			
		||||
  denotify: 'Unsubscribe from {account}',
 | 
			
		||||
  subscribedAccount: 'Subscribed to account',
 | 
			
		||||
  unsubscribedAccount: 'Unsubscribed to account',
 | 
			
		||||
  unsubscribedAccount: 'Unsubscribed from account',
 | 
			
		||||
  unblock: 'Unblock',
 | 
			
		||||
  nameAndFollowing: 'Name and following',
 | 
			
		||||
  clickToSeeAvatar: 'Click to see avatar',
 | 
			
		||||
| 
						 | 
				
			
			@ -474,6 +474,7 @@ export default {
 | 
			
		|||
  newFollowers: 'New followers',
 | 
			
		||||
  reblogs: 'Boosts',
 | 
			
		||||
  pollResults: 'Poll results',
 | 
			
		||||
  subscriptions: 'Subscribed toots',
 | 
			
		||||
  needToReauthenticate: 'You need to reauthenticate in order to enable push notification. Log out of {instance}?',
 | 
			
		||||
  failedToUpdatePush: 'Failed to update push notification settings: {error}',
 | 
			
		||||
  // Themes
 | 
			
		||||
| 
						 | 
				
			
			@ -508,6 +509,7 @@ export default {
 | 
			
		|||
  rebloggedYou: 'boosted your toot',
 | 
			
		||||
  favoritedYou: 'favorited your toot',
 | 
			
		||||
  followedYou: 'followed you',
 | 
			
		||||
  posted: 'posted',
 | 
			
		||||
  pollYouCreatedEnded: 'A poll you created has ended',
 | 
			
		||||
  pollYouVotedEnded: 'A poll you voted on has ended',
 | 
			
		||||
  reblogged: 'boosted',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ import { toast } from '../_components/toast/toast.js'
 | 
			
		|||
import { updateLocalRelationship } from './accounts.js'
 | 
			
		||||
import { formatIntl } from '../_utils/formatIntl.js'
 | 
			
		||||
 | 
			
		||||
export async function setAccountNotify (accountId, notify, toastOnSuccess) {
 | 
			
		||||
export async function setAccountNotified (accountId, notify, toastOnSuccess) {
 | 
			
		||||
  const { currentInstance, accessToken } = store.get()
 | 
			
		||||
  try {
 | 
			
		||||
    let relationship
 | 
			
		||||
| 
						 | 
				
			
			@ -15,11 +15,11 @@ export async function setAccountNotify (accountId, notify, toastOnSuccess) {
 | 
			
		|||
    }
 | 
			
		||||
    await updateLocalRelationship(currentInstance, accountId, relationship)
 | 
			
		||||
    if (toastOnSuccess) {
 | 
			
		||||
      /* no await */ toast.say(follow ? 'intl.subscribedAccount' : 'intl.unsubscribedAccount')
 | 
			
		||||
      /* no await */ toast.say(notify ? 'intl.subscribedAccount' : 'intl.unsubscribedAccount')
 | 
			
		||||
    }
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    console.error(e)
 | 
			
		||||
    /* no await */ toast.say(follow
 | 
			
		||||
    /* no await */ toast.say(notify
 | 
			
		||||
      ? formatIntl('intl.unableToSubscribe', { error: (e.message || '') })
 | 
			
		||||
      : formatIntl('intl.unableToUnsubscribe', { error: (e.message || '') })
 | 
			
		||||
    )
 | 
			
		||||
| 
						 | 
				
			
			@ -4,13 +4,13 @@ import { auth, basename } from './utils.js'
 | 
			
		|||
export async function notifyAccount (instanceName, accessToken, accountId) {
 | 
			
		||||
  const url = `${basename(instanceName)}/api/v1/accounts/${accountId}/follow`
 | 
			
		||||
  return post(url, {
 | 
			
		||||
    "notify": true,
 | 
			
		||||
    notify: true
 | 
			
		||||
  }, auth(accessToken), { timeout: WRITE_TIMEOUT })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function denotifyAccount (instanceName, accessToken, accountId) {
 | 
			
		||||
  const url = `${basename(instanceName)}/api/v1/accounts/${accountId}/follow`
 | 
			
		||||
  return post(url, {
 | 
			
		||||
    "notify": false
 | 
			
		||||
    notify: false
 | 
			
		||||
  }, auth(accessToken), { timeout: WRITE_TIMEOUT })
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ import { composeNewStatusMentioning } from '../../../_actions/mention.js'
 | 
			
		|||
import { toggleMute } from '../../../_actions/toggleMute.js'
 | 
			
		||||
import { reportStatusOrAccount } from '../../../_actions/report.js'
 | 
			
		||||
import { formatIntl } from '../../../_utils/formatIntl.js'
 | 
			
		||||
import { setAccountNotified } from '../../../_actions/setAccountNotified.js'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  oncreate,
 | 
			
		||||
| 
						 | 
				
			
			@ -39,6 +40,7 @@ export default {
 | 
			
		|||
    username: ({ account }) => account.username,
 | 
			
		||||
    muting: ({ relationship }) => relationship && relationship.muting,
 | 
			
		||||
    blocking: ({ relationship }) => relationship && relationship.blocking,
 | 
			
		||||
    notifying: ({ relationship }) => relationship && relationship.notifying,
 | 
			
		||||
    followLabel: ({ following, followRequested, account, username }) => {
 | 
			
		||||
      if (typeof following === 'undefined' || !account) {
 | 
			
		||||
        return ''
 | 
			
		||||
| 
						 | 
				
			
			@ -86,7 +88,7 @@ export default {
 | 
			
		|||
      blockLabel, blocking, blockIcon, muteLabel, muteIcon,
 | 
			
		||||
      followLabel, followIcon, following, followRequested,
 | 
			
		||||
      accountId, verifyCredentialsId, username, isUser, showReblogsLabel,
 | 
			
		||||
      domain, blockDomainLabel, reportLabel
 | 
			
		||||
      domain, blockDomainLabel, reportLabel, notifying
 | 
			
		||||
    }) => ([
 | 
			
		||||
      !isUser && {
 | 
			
		||||
        key: 'mention',
 | 
			
		||||
| 
						 | 
				
			
			@ -98,6 +100,16 @@ export default {
 | 
			
		|||
        label: followLabel,
 | 
			
		||||
        icon: followIcon
 | 
			
		||||
      },
 | 
			
		||||
      !isUser && following && notifying === false && { // notifying could be undefined for old servers
 | 
			
		||||
        key: 'notify',
 | 
			
		||||
        label: formatIntl('intl.notify', { account: `@${username}` }),
 | 
			
		||||
        icon: '#fa-bell'
 | 
			
		||||
      },
 | 
			
		||||
      !isUser && following && notifying === true && { // notifying could be undefined for old servers
 | 
			
		||||
        key: 'denotify',
 | 
			
		||||
        label: formatIntl('intl.denotify', { account: `@${username}` }),
 | 
			
		||||
        icon: '#fa-bell-o'
 | 
			
		||||
      },
 | 
			
		||||
      !isUser && {
 | 
			
		||||
        key: 'block',
 | 
			
		||||
        label: blockLabel,
 | 
			
		||||
| 
						 | 
				
			
			@ -151,6 +163,10 @@ export default {
 | 
			
		|||
          return this.onCopyClicked()
 | 
			
		||||
        case 'report':
 | 
			
		||||
          return this.onReport()
 | 
			
		||||
        case 'notify':
 | 
			
		||||
          return this.onNotifyClicked()
 | 
			
		||||
        case 'denotify':
 | 
			
		||||
          return this.onDenotifyClicked()
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    async onMentionClicked () {
 | 
			
		||||
| 
						 | 
				
			
			@ -193,6 +209,16 @@ export default {
 | 
			
		|||
      const { account } = this.get()
 | 
			
		||||
      this.close()
 | 
			
		||||
      await reportStatusOrAccount({ account })
 | 
			
		||||
    },
 | 
			
		||||
    async onNotifyClicked () {
 | 
			
		||||
      const { accountId } = this.get()
 | 
			
		||||
      this.close()
 | 
			
		||||
      await setAccountNotified(accountId, /* notify */ true, /* toastOnSuccess */ true)
 | 
			
		||||
    },
 | 
			
		||||
    async onDenotifyClicked () {
 | 
			
		||||
      const { accountId } = this.get()
 | 
			
		||||
      this.close()
 | 
			
		||||
      await setAccountNotified(accountId, /* notify */ false, /* toastOnSuccess */ true)
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  components: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,7 @@ import { shareStatus } from '../../../_actions/share.js'
 | 
			
		|||
import { toggleMute } from '../../../_actions/toggleMute.js'
 | 
			
		||||
import { reportStatusOrAccount } from '../../../_actions/report.js'
 | 
			
		||||
import { formatIntl } from '../../../_utils/formatIntl.js'
 | 
			
		||||
import { setAccountNotified } from '../../../_actions/setAccountNotified.js'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  oncreate,
 | 
			
		||||
| 
						 | 
				
			
			@ -53,6 +54,7 @@ export default {
 | 
			
		|||
    username: ({ account }) => account.username,
 | 
			
		||||
    muting: ({ relationship }) => relationship.muting,
 | 
			
		||||
    blocking: ({ relationship }) => relationship.blocking,
 | 
			
		||||
    notifying: ({ relationship }) => relationship && relationship.notifying,
 | 
			
		||||
    followLabel: ({ following, followRequested, account, username }) => {
 | 
			
		||||
      if (typeof following === 'undefined' || !account) {
 | 
			
		||||
        return ''
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +98,8 @@ export default {
 | 
			
		|||
    items: ({
 | 
			
		||||
      blockLabel, blocking, blockIcon, muteLabel, muteIcon, followLabel, followIcon,
 | 
			
		||||
      following, followRequested, pinLabel, isUser, visibility, mentionsUser, mutingConversation,
 | 
			
		||||
      muteConversationLabel, muteConversationIcon, supportsWebShare, isPublicOrUnlisted, bookmarkLabel
 | 
			
		||||
      muteConversationLabel, muteConversationIcon, supportsWebShare, isPublicOrUnlisted, bookmarkLabel,
 | 
			
		||||
      username, notifying
 | 
			
		||||
    }) => ([
 | 
			
		||||
      isUser && {
 | 
			
		||||
        key: 'delete',
 | 
			
		||||
| 
						 | 
				
			
			@ -113,6 +116,16 @@ export default {
 | 
			
		|||
        label: followLabel,
 | 
			
		||||
        icon: followIcon
 | 
			
		||||
      },
 | 
			
		||||
      !isUser && following && notifying === false && { // notifying could be undefined for old servers
 | 
			
		||||
        key: 'notify',
 | 
			
		||||
        label: formatIntl('intl.notify', { account: `@${username}` }),
 | 
			
		||||
        icon: '#fa-bell'
 | 
			
		||||
      },
 | 
			
		||||
      !isUser && following && notifying === true && { // notifying could be undefined for old servers
 | 
			
		||||
        key: 'denotify',
 | 
			
		||||
        label: formatIntl('intl.denotify', { account: `@${username}` }),
 | 
			
		||||
        icon: '#fa-bell-o'
 | 
			
		||||
      },
 | 
			
		||||
      !isUser && {
 | 
			
		||||
        key: 'block',
 | 
			
		||||
        label: blockLabel,
 | 
			
		||||
| 
						 | 
				
			
			@ -187,6 +200,10 @@ export default {
 | 
			
		|||
          return this.onReport()
 | 
			
		||||
        case 'bookmark':
 | 
			
		||||
          return this.onBookmark()
 | 
			
		||||
        case 'notify':
 | 
			
		||||
          return this.onNotifyClicked()
 | 
			
		||||
        case 'denotify':
 | 
			
		||||
          return this.onDenotifyClicked()
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    async onDeleteClicked () {
 | 
			
		||||
| 
						 | 
				
			
			@ -244,6 +261,16 @@ export default {
 | 
			
		|||
      const { status } = this.get()
 | 
			
		||||
      this.close()
 | 
			
		||||
      await setStatusBookmarkedOrUnbookmarked(status.id, !status.bookmarked)
 | 
			
		||||
    },
 | 
			
		||||
    async onNotifyClicked () {
 | 
			
		||||
      const { accountId } = this.get()
 | 
			
		||||
      this.close()
 | 
			
		||||
      await setAccountNotified(accountId, /* notify */ true, /* toastOnSuccess */ true)
 | 
			
		||||
    },
 | 
			
		||||
    async onDenotifyClicked () {
 | 
			
		||||
      const { accountId } = this.get()
 | 
			
		||||
      this.close()
 | 
			
		||||
      await setAccountNotified(accountId, /* notify */ false, /* toastOnSuccess */ true)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,6 @@
 | 
			
		|||
  <div class="account-profile-grid-wrapper">
 | 
			
		||||
    <div class="account-profile-grid">
 | 
			
		||||
      <AccountProfileHeader {account} {relationship} {verifyCredentials} />
 | 
			
		||||
      <AccountProfileNotify {account} {relationship} {verifyCredentials} />
 | 
			
		||||
      <AccountProfileFollow {account} {relationship} {verifyCredentials} />
 | 
			
		||||
      <AccountProfileNote {account} />
 | 
			
		||||
      <AccountProfileMeta {account} />
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +37,7 @@
 | 
			
		|||
    display: grid;
 | 
			
		||||
    grid-template-areas: "avatar     name        label     followed-by   follow"
 | 
			
		||||
                         "avatar     username    username  username      follow"
 | 
			
		||||
                         "avatar     note        note      note          notify"
 | 
			
		||||
                         "avatar     note        note      note          follow"
 | 
			
		||||
                         "meta       meta        meta      meta          meta"
 | 
			
		||||
                         "details    details     details   details       details";
 | 
			
		||||
    grid-template-columns: min-content auto 1fr 1fr min-content;
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +71,7 @@
 | 
			
		|||
      grid-template-areas: "avatar     name          follow"
 | 
			
		||||
                           "avatar     label         follow"
 | 
			
		||||
                           "avatar     username      follow"
 | 
			
		||||
                           "avatar     followed-by   notify"
 | 
			
		||||
                           "avatar     followed-by   follow"
 | 
			
		||||
                           "note       note          note"
 | 
			
		||||
                           "meta       meta          meta"
 | 
			
		||||
                           "details    details       details";
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +97,6 @@
 | 
			
		|||
                           "username    username"
 | 
			
		||||
                           "followed-by followed-by"
 | 
			
		||||
                           "follow      follow"
 | 
			
		||||
                           "notify      notify"
 | 
			
		||||
                           "note        note"
 | 
			
		||||
                           "meta        meta"
 | 
			
		||||
                           "details     details";
 | 
			
		||||
| 
						 | 
				
			
			@ -110,7 +108,6 @@
 | 
			
		|||
</style>
 | 
			
		||||
<script>
 | 
			
		||||
  import AccountProfileHeader from './AccountProfileHeader.html'
 | 
			
		||||
  import AccountProfileNotify from './AccountProfileNotify.html'
 | 
			
		||||
  import AccountProfileFollow from './AccountProfileFollow.html'
 | 
			
		||||
  import AccountProfileNote from './AccountProfileNote.html'
 | 
			
		||||
  import AccountProfileMeta from './AccountProfileMeta.html'
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +144,6 @@
 | 
			
		|||
      AccountProfileHeader,
 | 
			
		||||
      AccountProfileFollow,
 | 
			
		||||
      AccountProfileNote,
 | 
			
		||||
      AccountProfileNotify,
 | 
			
		||||
      AccountProfileMeta,
 | 
			
		||||
      AccountProfileDetails,
 | 
			
		||||
      AccountProfileMovedBanner,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,92 +0,0 @@
 | 
			
		|||
<div class="account-profile-notify {shown ? 'shown' : ''}">
 | 
			
		||||
  <!--
 | 
			
		||||
    The bell notification button (Mastodon 3.3+)
 | 
			
		||||
    Shows if we're getting notifications or not.
 | 
			
		||||
    It is not possible to turn on notifications for accounts you don't follow.
 | 
			
		||||
    Also the instance can just have no support for this feature.
 | 
			
		||||
  -->
 | 
			
		||||
  <IconButton
 | 
			
		||||
    className="account-profile-notify-icon-button"
 | 
			
		||||
    {label}
 | 
			
		||||
    pressedLabel="{intl.denotifyLabel}"
 | 
			
		||||
    {href}
 | 
			
		||||
    big={!$isVeryTinyMobileSize}
 | 
			
		||||
    on:click="onNotifyButtonClick(event)"
 | 
			
		||||
    ref:icon
 | 
			
		||||
  />
 | 
			
		||||
</div>
 | 
			
		||||
<style>
 | 
			
		||||
  .account-profile-notify {
 | 
			
		||||
    grid-area: notify;
 | 
			
		||||
    align-self: flex-start;
 | 
			
		||||
    display: none;
 | 
			
		||||
  }
 | 
			
		||||
  .account-profile-notify.shown {
 | 
			
		||||
    display: block;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @media (max-width: 240px) {
 | 
			
		||||
    .account-profile-notify {
 | 
			
		||||
      justify-self: flex-end;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</style>
 | 
			
		||||
<script>
 | 
			
		||||
  import IconButton from '../IconButton.html'
 | 
			
		||||
  import { FOLLOW_BUTTON_ANIMATION } from '../../_static/animations.js'
 | 
			
		||||
  import { store } from '../../_store/store.js'
 | 
			
		||||
  import { setAccountNotify } from '../../_actions/notify.js'
 | 
			
		||||
  import { formatIntl } from '../../_utils/formatIntl.js'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    methods: {
 | 
			
		||||
      oncreate () {
 | 
			
		||||
        if (process.browser) {
 | 
			
		||||
          window.__button = this
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      async onNotifyButtonClick (e) {
 | 
			
		||||
        e.preventDefault()
 | 
			
		||||
        e.stopPropagation()
 | 
			
		||||
        const {
 | 
			
		||||
          account,
 | 
			
		||||
          accountId,
 | 
			
		||||
          notifying
 | 
			
		||||
        } = this.get()
 | 
			
		||||
        if (notifying) { // unblock
 | 
			
		||||
          await setAccountNotify(accountId, false)
 | 
			
		||||
        } else { // follow/unfollow
 | 
			
		||||
          this.refs.icon.animate(FOLLOW_BUTTON_ANIMATION)
 | 
			
		||||
          await setAccountNotify(accountId, true)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    store: () => store,
 | 
			
		||||
    data: () => ({
 | 
			
		||||
    }),
 | 
			
		||||
    computed: {
 | 
			
		||||
      accountId: ({ account }) => account.id,
 | 
			
		||||
      notifying: ({ relationship }) => {
 | 
			
		||||
        return relationship && relationship.notifying
 | 
			
		||||
      },
 | 
			
		||||
      href: ({ notifying }) => {
 | 
			
		||||
        if (notifying) {
 | 
			
		||||
          return '#fa-bell-ringing'
 | 
			
		||||
        }
 | 
			
		||||
        return '#fa-bell-o'
 | 
			
		||||
      },
 | 
			
		||||
      label: ({ notifying }) => {
 | 
			
		||||
        if (notifying) {
 | 
			
		||||
          return formatIntl('intl.notifyLabel')
 | 
			
		||||
        }
 | 
			
		||||
        return formatIntl('intl.denotifyLabel')
 | 
			
		||||
      },
 | 
			
		||||
      shown: ({ verifyCredentials, relationship }) => (
 | 
			
		||||
        verifyCredentials && relationship && verifyCredentials.id !== relationship.id && relationship.following
 | 
			
		||||
      ),
 | 
			
		||||
    },
 | 
			
		||||
    components: {
 | 
			
		||||
      IconButton
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +10,8 @@
 | 
			
		|||
    NOTIFICATION_FAVORITES,
 | 
			
		||||
    NOTIFICATION_FOLLOWS,
 | 
			
		||||
    NOTIFICATION_MENTIONS,
 | 
			
		||||
    NOTIFICATION_POLLS
 | 
			
		||||
    NOTIFICATION_POLLS,
 | 
			
		||||
    NOTIFICATION_SUBSCRIPTIONS
 | 
			
		||||
  } from '../../../_static/instanceSettings.js'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
| 
						 | 
				
			
			@ -40,6 +41,11 @@
 | 
			
		|||
          key: NOTIFICATION_POLLS,
 | 
			
		||||
          label: 'intl.pollResults',
 | 
			
		||||
          defaultValue: true
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          key: NOTIFICATION_SUBSCRIPTIONS,
 | 
			
		||||
          label: 'intl.subscriptions',
 | 
			
		||||
          defaultValue: true
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    }),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -76,6 +76,10 @@
 | 
			
		|||
        {
 | 
			
		||||
          key: 'poll',
 | 
			
		||||
          label: 'intl.pollResults'
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          key: 'status',
 | 
			
		||||
          label: 'intl.subscriptions'
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    }),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -295,7 +295,7 @@
 | 
			
		|||
        )
 | 
			
		||||
      ),
 | 
			
		||||
      showHeader: ({ notification, status, timelineType }) => (
 | 
			
		||||
        (notification && ['reblog', 'favourite', 'poll'].includes(notification.type)) ||
 | 
			
		||||
        (notification && ['reblog', 'favourite', 'poll', 'status'].includes(notification.type)) ||
 | 
			
		||||
        status.reblog ||
 | 
			
		||||
        timelineType === 'pinned'
 | 
			
		||||
      ),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -125,6 +125,8 @@
 | 
			
		|||
          return '#fa-user-plus'
 | 
			
		||||
        } else if (notificationType === 'poll') {
 | 
			
		||||
          return '#fa-bar-chart'
 | 
			
		||||
        } else if (notificationType === 'status') {
 | 
			
		||||
          return '#fa-comment'
 | 
			
		||||
        }
 | 
			
		||||
        return '#fa-star'
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			@ -135,6 +137,8 @@
 | 
			
		|||
          return 'intl.favoritedYou'
 | 
			
		||||
        } else if (notificationType === 'follow') {
 | 
			
		||||
          return 'intl.followedYou'
 | 
			
		||||
        } else if (notificationType === 'status') {
 | 
			
		||||
          return 'intl.posted'
 | 
			
		||||
        } else if (notificationType === 'poll') {
 | 
			
		||||
          if ($currentVerifyCredentials && status && $currentVerifyCredentials.id === status.account.id) {
 | 
			
		||||
            return 'intl.pollYouCreatedEnded'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,3 +6,4 @@ export const NOTIFICATION_FAVORITES = 'notificationFavs'
 | 
			
		|||
export const NOTIFICATION_FOLLOWS = 'notificationFollows'
 | 
			
		||||
export const NOTIFICATION_MENTIONS = 'notificationMentions'
 | 
			
		||||
export const NOTIFICATION_POLLS = 'notificationPolls'
 | 
			
		||||
export const NOTIFICATION_SUBSCRIPTIONS = 'notificationSubscriptions'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ import {
 | 
			
		|||
  HOME_REPLIES,
 | 
			
		||||
  NOTIFICATION_FAVORITES,
 | 
			
		||||
  NOTIFICATION_FOLLOWS, NOTIFICATION_MENTIONS, NOTIFICATION_POLLS,
 | 
			
		||||
  NOTIFICATION_REBLOGS
 | 
			
		||||
  NOTIFICATION_REBLOGS, NOTIFICATION_SUBSCRIPTIONS
 | 
			
		||||
} from '../../_static/instanceSettings.js'
 | 
			
		||||
import {
 | 
			
		||||
  WORD_FILTER_CONTEXT_ACCOUNT,
 | 
			
		||||
| 
						 | 
				
			
			@ -46,12 +46,14 @@ export function timelineFilterComputations (store) {
 | 
			
		|||
  computeTimelineFilter(store, 'timelineShowFavs', { notifications: NOTIFICATION_FAVORITES })
 | 
			
		||||
  computeTimelineFilter(store, 'timelineShowMentions', { notifications: NOTIFICATION_MENTIONS })
 | 
			
		||||
  computeTimelineFilter(store, 'timelineShowPolls', { notifications: NOTIFICATION_POLLS })
 | 
			
		||||
  computeTimelineFilter(store, 'timelineShowSubscriptions', { notifications: NOTIFICATION_SUBSCRIPTIONS })
 | 
			
		||||
 | 
			
		||||
  computeNotificationFilter(store, 'timelineNotificationShowReblogs', NOTIFICATION_REBLOGS)
 | 
			
		||||
  computeNotificationFilter(store, 'timelineNotificationShowFollows', NOTIFICATION_FOLLOWS)
 | 
			
		||||
  computeNotificationFilter(store, 'timelineNotificationShowFavs', NOTIFICATION_FAVORITES)
 | 
			
		||||
  computeNotificationFilter(store, 'timelineNotificationShowMentions', NOTIFICATION_MENTIONS)
 | 
			
		||||
  computeNotificationFilter(store, 'timelineNotificationShowPolls', NOTIFICATION_POLLS)
 | 
			
		||||
  computeNotificationFilter(store, 'timelineNotificationShowSubscriptions', NOTIFICATION_SUBSCRIPTIONS)
 | 
			
		||||
 | 
			
		||||
  store.compute(
 | 
			
		||||
    'timelineWordFilterContext',
 | 
			
		||||
| 
						 | 
				
			
			@ -85,10 +87,10 @@ export function timelineFilterComputations (store) {
 | 
			
		|||
    [
 | 
			
		||||
      'timelineShowReblogs', 'timelineShowReplies', 'timelineShowFollows',
 | 
			
		||||
      'timelineShowFavs', 'timelineShowMentions', 'timelineShowPolls',
 | 
			
		||||
      'timelineWordFilterContext'
 | 
			
		||||
      'timelineShowSubscriptions', 'timelineWordFilterContext'
 | 
			
		||||
    ],
 | 
			
		||||
    (showReblogs, showReplies, showFollows, showFavs, showMentions, showPolls, wordFilterContext) => (
 | 
			
		||||
      createFilterFunction(showReblogs, showReplies, showFollows, showFavs, showMentions, showPolls, wordFilterContext)
 | 
			
		||||
    (showReblogs, showReplies, showFollows, showFavs, showMentions, showPolls, showSubscriptions, wordFilterContext) => (
 | 
			
		||||
      createFilterFunction(showReblogs, showReplies, showFollows, showFavs, showMentions, showPolls, showSubscriptions, wordFilterContext)
 | 
			
		||||
    )
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -99,10 +101,10 @@ export function timelineFilterComputations (store) {
 | 
			
		|||
    [
 | 
			
		||||
      'timelineNotificationShowReblogs', 'timelineNotificationShowFollows',
 | 
			
		||||
      'timelineNotificationShowFavs', 'timelineNotificationShowMentions',
 | 
			
		||||
      'timelineNotificationShowPolls'
 | 
			
		||||
      'timelineNotificationShowPolls', 'timelineNotificationShowSubscriptions'
 | 
			
		||||
    ],
 | 
			
		||||
    (showReblogs, showFollows, showFavs, showMentions, showPolls) => (
 | 
			
		||||
      createFilterFunction(showReblogs, true, showFollows, showFavs, showMentions, showPolls, WORD_FILTER_CONTEXT_NOTIFICATIONS)
 | 
			
		||||
    (showReblogs, showFollows, showFavs, showMentions, showPolls, showSubscriptions) => (
 | 
			
		||||
      createFilterFunction(showReblogs, true, showFollows, showFavs, showMentions, showPolls, showSubscriptions, WORD_FILTER_CONTEXT_NOTIFICATIONS)
 | 
			
		||||
    )
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,8 @@
 | 
			
		|||
// create a function for filtering timeline item summaries
 | 
			
		||||
 | 
			
		||||
export const createFilterFunction = (
 | 
			
		||||
  showReblogs, showReplies, showFollows, showFavs, showMentions, showPolls, wordFilterContext
 | 
			
		||||
  showReblogs, showReplies, showFollows, showFavs, showMentions, showPolls,
 | 
			
		||||
  showSubscriptions, wordFilterContext
 | 
			
		||||
) => {
 | 
			
		||||
  return item => {
 | 
			
		||||
    if (item.filterContexts && item.filterContexts.includes(wordFilterContext)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -19,6 +20,8 @@ export const createFilterFunction = (
 | 
			
		|||
        return showMentions
 | 
			
		||||
      case 'follow':
 | 
			
		||||
        return showFollows
 | 
			
		||||
      case 'status':
 | 
			
		||||
        return showSubscriptions
 | 
			
		||||
    }
 | 
			
		||||
    if (item.reblogId) {
 | 
			
		||||
      return showReblogs
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -214,6 +214,7 @@ async function showRichNotification (data, notification) {
 | 
			
		|||
    }
 | 
			
		||||
    case 'reblog':
 | 
			
		||||
    case 'favourite':
 | 
			
		||||
    case 'status':
 | 
			
		||||
    case 'poll': {
 | 
			
		||||
      await self.registration.showNotification(data.title, {
 | 
			
		||||
        badge,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +1,18 @@
 | 
			
		|||
import { favoriteStatus } from '../src/routes/_api/favorite'
 | 
			
		||||
import { favoriteStatus } from '../src/routes/_api/favorite.js'
 | 
			
		||||
import fetch from 'node-fetch'
 | 
			
		||||
import FileApi from 'file-api'
 | 
			
		||||
import { users } from './users'
 | 
			
		||||
import { postStatus } from '../src/routes/_api/statuses'
 | 
			
		||||
import { deleteStatus } from '../src/routes/_api/delete'
 | 
			
		||||
import { authorizeFollowRequest, getFollowRequests } from '../src/routes/_api/followRequests'
 | 
			
		||||
import { followAccount, unfollowAccount } from '../src/routes/_api/follow'
 | 
			
		||||
import { updateCredentials } from '../src/routes/_api/updateCredentials'
 | 
			
		||||
import { reblogStatus } from '../src/routes/_api/reblog'
 | 
			
		||||
import { users } from './users.js'
 | 
			
		||||
import { postStatus } from '../src/routes/_api/statuses.js'
 | 
			
		||||
import { deleteStatus } from '../src/routes/_api/delete.js'
 | 
			
		||||
import { authorizeFollowRequest, getFollowRequests } from '../src/routes/_api/followRequests.js'
 | 
			
		||||
import { followAccount, unfollowAccount } from '../src/routes/_api/follow.js'
 | 
			
		||||
import { updateCredentials } from '../src/routes/_api/updateCredentials.js'
 | 
			
		||||
import { reblogStatus } from '../src/routes/_api/reblog.js'
 | 
			
		||||
import { submitMedia } from './submitMedia.js'
 | 
			
		||||
import { voteOnPoll } from '../src/routes/_api/polls'
 | 
			
		||||
import { POLL_EXPIRY_DEFAULT } from '../src/routes/_static/polls'
 | 
			
		||||
import { createList, getLists } from '../src/routes/_api/lists'
 | 
			
		||||
import { createFilter, deleteFilter, getFilters } from '../src/routes/_api/filters'
 | 
			
		||||
import { voteOnPoll } from '../src/routes/_api/polls.js'
 | 
			
		||||
import { POLL_EXPIRY_DEFAULT } from '../src/routes/_static/polls.js'
 | 
			
		||||
import { createList, getLists } from '../src/routes/_api/lists.js'
 | 
			
		||||
import { createFilter, deleteFilter, getFilters } from '../src/routes/_api/filters.js'
 | 
			
		||||
 | 
			
		||||
global.fetch = fetch
 | 
			
		||||
global.File = FileApi.File
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,7 +2,7 @@ import {
 | 
			
		|||
  accountProfileFollowButton,
 | 
			
		||||
  accountProfileFollowedBy, accountProfileMoreOptionsButton, communityNavButton, getNthSearchResult,
 | 
			
		||||
  getNthStatus, getNthStatusOptionsButton, getNthDialogOptionsOption, getUrl, modalDialog,
 | 
			
		||||
  sleep
 | 
			
		||||
  sleep, getDialogOptionWithText
 | 
			
		||||
} from '../utils'
 | 
			
		||||
import { Selector as $ } from 'testcafe'
 | 
			
		||||
import { loginAsFoobar } from '../roles'
 | 
			
		||||
| 
						 | 
				
			
			@ -21,10 +21,10 @@ test('Can block and unblock an account from a status', async t => {
 | 
			
		|||
  await t
 | 
			
		||||
    .click(getNthStatusOptionsButton(1))
 | 
			
		||||
    .expect(getNthDialogOptionsOption(1).innerText).contains('Unfollow @admin')
 | 
			
		||||
    .expect(getNthDialogOptionsOption(2).innerText).contains('Block @admin')
 | 
			
		||||
    .expect(getNthDialogOptionsOption(3).innerText).contains('Block @admin')
 | 
			
		||||
  await sleep(500)
 | 
			
		||||
  await t
 | 
			
		||||
    .click(getNthDialogOptionsOption(2))
 | 
			
		||||
    .click(getDialogOptionWithText('Block @admin'))
 | 
			
		||||
    .expect(modalDialog.exists).notOk()
 | 
			
		||||
  await sleep(500)
 | 
			
		||||
  await t
 | 
			
		||||
| 
						 | 
				
			
			@ -60,12 +60,9 @@ test('Can block and unblock an account from the account profile page', async t =
 | 
			
		|||
  await sleep(500)
 | 
			
		||||
  await t
 | 
			
		||||
    .click(accountProfileMoreOptionsButton)
 | 
			
		||||
    .expect(getNthDialogOptionsOption(1).innerText).contains('Mention @baz')
 | 
			
		||||
    .expect(getNthDialogOptionsOption(2).innerText).contains('Follow @baz')
 | 
			
		||||
    .expect(getNthDialogOptionsOption(3).innerText).contains('Block @baz')
 | 
			
		||||
  await sleep(500)
 | 
			
		||||
  await t
 | 
			
		||||
    .click(getNthDialogOptionsOption(3))
 | 
			
		||||
    .click(getDialogOptionWithText('Block @baz'))
 | 
			
		||||
    .expect(accountProfileFollowedBy.innerText).match(/blocked/i)
 | 
			
		||||
    .expect(accountProfileFollowButton.getAttribute('aria-label')).eql('Unblock')
 | 
			
		||||
    .expect(accountProfileFollowButton.getAttribute('title')).eql('Unblock')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,11 +5,10 @@ import {
 | 
			
		|||
  getNthSearchResult,
 | 
			
		||||
  getNthStatus,
 | 
			
		||||
  getNthStatusOptionsButton,
 | 
			
		||||
  getNthDialogOptionsOption,
 | 
			
		||||
  getUrl,
 | 
			
		||||
  modalDialog,
 | 
			
		||||
  closeDialogButton,
 | 
			
		||||
  confirmationDialogOKButton, sleep
 | 
			
		||||
  confirmationDialogOKButton, sleep, getDialogOptionWithText
 | 
			
		||||
} from '../utils'
 | 
			
		||||
import { Selector as $ } from 'testcafe'
 | 
			
		||||
import { loginAsFoobar } from '../roles'
 | 
			
		||||
| 
						 | 
				
			
			@ -25,12 +24,9 @@ test('Can mute and unmute an account', async t => {
 | 
			
		|||
 | 
			
		||||
  await t.expect(getNthStatus(1).innerText).contains(post, { timeout: 20000 })
 | 
			
		||||
    .click(getNthStatusOptionsButton(1))
 | 
			
		||||
    .expect(getNthDialogOptionsOption(1).innerText).contains('Unfollow @admin')
 | 
			
		||||
    .expect(getNthDialogOptionsOption(2).innerText).contains('Block @admin')
 | 
			
		||||
    .expect(getNthDialogOptionsOption(3).innerText).contains('Mute @admin')
 | 
			
		||||
  await sleep(1000)
 | 
			
		||||
  await t
 | 
			
		||||
    .click(getNthDialogOptionsOption(3))
 | 
			
		||||
    .click(getDialogOptionWithText('Mute @admin'))
 | 
			
		||||
  await sleep(1000)
 | 
			
		||||
  await t
 | 
			
		||||
    .click(confirmationDialogOKButton)
 | 
			
		||||
| 
						 | 
				
			
			@ -43,20 +39,13 @@ test('Can mute and unmute an account', async t => {
 | 
			
		|||
    .click(getNthSearchResult(1))
 | 
			
		||||
    .expect(getUrl()).contains('/accounts/1')
 | 
			
		||||
    .click(accountProfileMoreOptionsButton)
 | 
			
		||||
    .expect(getNthDialogOptionsOption(1).innerText).contains('Mention @admin')
 | 
			
		||||
    .expect(getNthDialogOptionsOption(2).innerText).contains('Unfollow @admin')
 | 
			
		||||
    .expect(getNthDialogOptionsOption(3).innerText).contains('Block @admin')
 | 
			
		||||
    .expect(getNthDialogOptionsOption(4).innerText).contains('Unmute @admin')
 | 
			
		||||
  await sleep(1000)
 | 
			
		||||
  await t
 | 
			
		||||
    .click(getNthDialogOptionsOption(4))
 | 
			
		||||
    .click(getDialogOptionWithText('Unmute @admin'))
 | 
			
		||||
  await sleep(1000)
 | 
			
		||||
  await t
 | 
			
		||||
    .click(accountProfileMoreOptionsButton)
 | 
			
		||||
    .expect(getNthDialogOptionsOption(1).innerText).contains('Mention @admin')
 | 
			
		||||
    .expect(getNthDialogOptionsOption(2).innerText).contains('Unfollow @admin')
 | 
			
		||||
    .expect(getNthDialogOptionsOption(3).innerText).contains('Block @admin')
 | 
			
		||||
    .expect(getNthDialogOptionsOption(4).innerText).contains('Mute @admin')
 | 
			
		||||
    .expect(getDialogOptionWithText('Mute @admin').exists).ok()
 | 
			
		||||
  await sleep(1000)
 | 
			
		||||
  await t
 | 
			
		||||
    .click(closeDialogButton)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
import {
 | 
			
		||||
  accountProfileMoreOptionsButton,
 | 
			
		||||
  getNthStatus,
 | 
			
		||||
  getNthStatusOptionsButton,
 | 
			
		||||
  getUrl,
 | 
			
		||||
  modalDialog,
 | 
			
		||||
  sleep, getDialogOptionWithText, getNthStatusAccountLink, notificationsNavButton, getNthStatusHeader
 | 
			
		||||
} from '../utils'
 | 
			
		||||
import { loginAsFoobar } from '../roles'
 | 
			
		||||
import { postAs } from '../serverActions'
 | 
			
		||||
 | 
			
		||||
fixture`139-notify-denotify.js`
 | 
			
		||||
  .page`http://localhost:4002`
 | 
			
		||||
 | 
			
		||||
test('Can notify and denotify an account', async t => {
 | 
			
		||||
  await loginAsFoobar(t)
 | 
			
		||||
  const post = 'ha ha ha'
 | 
			
		||||
  await postAs('admin', post)
 | 
			
		||||
 | 
			
		||||
  await t.expect(getNthStatus(1).innerText).contains(post, { timeout: 20000 })
 | 
			
		||||
    .click(getNthStatusOptionsButton(1))
 | 
			
		||||
  await sleep(1000)
 | 
			
		||||
  await t.click(getDialogOptionWithText('Subscribe to @admin'))
 | 
			
		||||
  await sleep(1000)
 | 
			
		||||
  await t
 | 
			
		||||
    .expect(modalDialog.exists).notOk()
 | 
			
		||||
  await sleep(1000)
 | 
			
		||||
  const notificationPost = 'get a notification for this'
 | 
			
		||||
  await postAs('admin', notificationPost)
 | 
			
		||||
  await sleep(1000)
 | 
			
		||||
  await t
 | 
			
		||||
    .expect(notificationsNavButton.getAttribute('aria-label')).eql('Notifications (1 notification)', {
 | 
			
		||||
      timeout: 20000
 | 
			
		||||
    })
 | 
			
		||||
    .click(notificationsNavButton)
 | 
			
		||||
    .expect(getUrl()).contains('/notifications')
 | 
			
		||||
  await t
 | 
			
		||||
    .expect(getNthStatus(1).innerText).contains(notificationPost, { timeout: 20000 })
 | 
			
		||||
    .expect(getNthStatusHeader(1).innerText).contains('posted')
 | 
			
		||||
    .click(getNthStatusAccountLink(1))
 | 
			
		||||
    .expect(getUrl()).contains('/accounts/1')
 | 
			
		||||
    .click(accountProfileMoreOptionsButton)
 | 
			
		||||
  await sleep(1000)
 | 
			
		||||
  await t.click(getDialogOptionWithText('Unsubscribe from @admin'))
 | 
			
		||||
  await sleep(1000)
 | 
			
		||||
  await t.click(accountProfileMoreOptionsButton)
 | 
			
		||||
  await t
 | 
			
		||||
    .expect(getDialogOptionWithText('Subscribe to @admin').exists).ok()
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			@ -522,6 +522,10 @@ export function getNthStatusOptionsButton (n) {
 | 
			
		|||
  return $(`${getNthStatusSelector(n)} .status-toolbar button:nth-child(4)`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getNthStatusAccountLink (n) {
 | 
			
		||||
  return $(`${getNthStatusSelector(n)} .status-author-name`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getNthFavoritedLabel (n) {
 | 
			
		||||
  return getNthFavoriteButton(n).getAttribute('aria-label')
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -546,6 +550,10 @@ export function getNthDialogOptionsOption (n) {
 | 
			
		|||
  return $(`.modal-dialog li:nth-child(${n}) button`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getDialogOptionWithText (text) {
 | 
			
		||||
  return $('.modal-dialog li button').withText(text)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getReblogsCount () {
 | 
			
		||||
  return reblogsCountElement.innerCount
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue