Merge remote-tracking branch 'origin/develop' into shigusegubu

* origin/develop:
  changelong
  Fix pinned statuses and perhaps some other stuff
  update changelog
  Remove title from index.html, title is now from server generated meta
  changelog mention
  fix emoji not working in profile field names
  change method of fix to rounding
  changelog
  prevent call to scroll if the value doesn't change because firefox is stupid
  Update CHANGELOG.md
  wrap react button icon to a span to fix popover overflow
  Update CHANGELOG.md to match master
  change favicon dimensions for high res, add handling when favicon isn't available
  remove the favicon changes
  make badge just a ball, make it use theming
  add favicon badge for unread notifs
This commit is contained in:
Henry Jameson 2020-11-17 20:44:06 +02:00
commit d8ac75f58a
10 changed files with 116 additions and 21 deletions

View file

@ -3,17 +3,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Fixed
- Fixed the occasional bug where screen would scroll 1px when typing into a reply form
- Fixed custom emoji not working in profile field names
- Fixed pinned statuses not appearing in user profiles
## [2.2.1] - 2020-11-11
### Fixed
- Fixed regression in react popup alignment and overflowing
## [2.2.0] - 2020-11-06
### Added ### Added
- New option to optimize timeline rendering to make the site more responsive (enabled by default) - New option to optimize timeline rendering to make the site more responsive (enabled by default)
- New instance option `logoLeft` to move logo to the left side in desktop nav bar - New instance option `logoLeft` to move logo to the left side in desktop nav bar
- Import/export a muted users - Import/export a muted users
- Proper handling of deletes when using websocket streaming - Proper handling of deletes when using websocket streaming
- Added optimistic chat message sending, so you can start writing next message before the previous one has been sent - Added optimistic chat message sending, so you can start writing next message before the previous one has been sent
- Added a small red badge to the favicon when there's unread notifications
### Fixed ### Fixed
- Fixed chats list not updating its order when new messages come in
- Fixed chat messages sometimes getting lost when you receive a message at the same time
- Fixed clicking NSFW hider through status popover - Fixed clicking NSFW hider through status popover
- Fixed chat-view back button being hard to click - Fixed chat-view back button being hard to click
- Fixed fresh chat notifications being cleared immediately while leaving the chat view and not having time to actually see the messages - Fixed fresh chat notifications being cleared immediately while leaving the chat view and not having time to actually see the messages
@ -29,6 +41,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Logo is now clickable - Logo is now clickable
- Changed default logo to SVG version - Changed default logo to SVG version
## [2.1.2] - 2020-09-17
### Fixed
- Fixed chats list not updating its order when new messages come in
- Fixed chat messages sometimes getting lost when you receive a message at the same time
## [2.1.1] - 2020-09-08 ## [2.1.1] - 2020-09-08
### Changed ### Changed
- Polls will be hidden with status content if "Collapse posts with subjects" is enabled and the post is collapsed. - Polls will be hidden with status content if "Collapse posts with subjects" is enabled and the post is collapsed.
@ -154,8 +173,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Ability to change user's email - Ability to change user's email
- About page - About page
- Added remote user redirect - Added remote user redirect
- Bookmarks
### Changed ### Changed
- changed the way fading effects for user profile/long statuses works, now uses css-mask instead of gradient background hacks which weren't exactly compatible with semi-transparent themes - changed the way fading effects for user profile/long statuses works, now uses css-mask instead of gradient background hacks which weren't exactly compatible with semi-transparent themes
### Fixed ### Fixed
- improved hotkey behavior on autocomplete popup - improved hotkey behavior on autocomplete popup

View file

@ -3,7 +3,6 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
<title>Pleroma</title>
<link rel="apple-touch-icon" sizes="180x180" href="/ngs/apple-touch-icon.png"> <link rel="apple-touch-icon" sizes="180x180" href="/ngs/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/ngs/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="32x32" href="/ngs/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/ngs/favicon-16x16.png"> <link rel="icon" type="image/png" sizes="16x16" href="/ngs/favicon-16x16.png">

View file

@ -7,6 +7,7 @@ import { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js'
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js' import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
import { applyTheme } from '../services/style_setter/style_setter.js' import { applyTheme } from '../services/style_setter/style_setter.js'
import FaviconService from '../services/favicon_service/favicon_service.js'
let staticInitialResults = null let staticInitialResults = null
@ -326,6 +327,8 @@ const afterStoreSetup = async ({ store, i18n }) => {
const width = windowWidth() const width = windowWidth()
store.dispatch('setMobileLayout', width <= 800) store.dispatch('setMobileLayout', width <= 800)
FaviconService.initFaviconService()
const overrides = window.___pleromafe_dev_overrides || {} const overrides = window.___pleromafe_dev_overrides || {}
const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin
store.dispatch('setInstanceOption', { name: 'server', value: server }) store.dispatch('setInstanceOption', { name: 'server', value: server })

View file

@ -6,6 +6,7 @@ import {
filteredNotificationsFromStore, filteredNotificationsFromStore,
unseenNotificationsFromStore unseenNotificationsFromStore
} from '../../services/notification_utils/notification_utils.js' } from '../../services/notification_utils/notification_utils.js'
import FaviconService from '../../services/favicon_service/favicon_service.js'
import { library } from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core'
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'
@ -75,8 +76,10 @@ const Notifications = {
watch: { watch: {
unseenCountTitle (count) { unseenCountTitle (count) {
if (count > 0) { if (count > 0) {
FaviconService.drawFaviconBadge()
this.$store.dispatch('setPageTitle', `(${count})`) this.$store.dispatch('setPageTitle', `(${count})`)
} else { } else {
FaviconService.clearFaviconBadge()
this.$store.dispatch('setPageTitle', '') this.$store.dispatch('setPageTitle', '')
} }
} }

View file

@ -531,7 +531,7 @@ const PostStatusForm = {
!(isFormBiggerThanScroller && !(isFormBiggerThanScroller &&
this.$refs.textarea.selectionStart !== this.$refs.textarea.value.length) this.$refs.textarea.selectionStart !== this.$refs.textarea.value.length)
const totalDelta = shouldScrollToBottom ? bottomChangeDelta : 0 const totalDelta = shouldScrollToBottom ? bottomChangeDelta : 0
const targetScroll = currentScroll + totalDelta const targetScroll = Math.round(currentScroll + totalDelta)
if (scrollerRef === window) { if (scrollerRef === window) {
scrollerRef.scroll(0, targetScroll) scrollerRef.scroll(0, targetScroll)

View file

@ -4,6 +4,7 @@
placement="top" placement="top"
:offset="{ y: 5 }" :offset="{ y: 5 }"
class="react-button-popover" class="react-button-popover"
:bound-to="{ x: 'container' }"
> >
<div <div
slot="content" slot="content"
@ -37,12 +38,13 @@
<div class="reaction-bottom-fader" /> <div class="reaction-bottom-fader" />
</div> </div>
</div> </div>
<span slot="trigger">
<FAIcon <FAIcon
slot="trigger"
class="fa-scale-110 fa-old-padding add-reaction-button" class="fa-scale-110 fa-old-padding add-reaction-button"
:icon="['far', 'smile-beam']" :icon="['far', 'smile-beam']"
:title="$t('tool_tip.add_reaction')" :title="$t('tool_tip.add_reaction')"
/> />
</span>
</Popover> </Popover>
</template> </template>

View file

@ -20,14 +20,13 @@
:key="index" :key="index"
class="user-profile-field" class="user-profile-field"
> >
<!-- eslint-disable vue/no-v-html -->
<dt <dt
:title="user.fields_text[index].name" :title="user.fields_text[index].name"
class="user-profile-field-name" class="user-profile-field-name"
@click.prevent="linkClicked" @click.prevent="linkClicked"
> v-html="field.name"
{{ field.name }} />
</dt>
<!-- eslint-disable vue/no-v-html -->
<dd <dd
:title="user.fields_text[index].value" :title="user.fields_text[index].value"
class="user-profile-field-value" class="user-profile-field-value"

View file

@ -137,11 +137,11 @@ export const mutations = {
}, },
saveFriendIds (state, { id, friendIds }) { saveFriendIds (state, { id, friendIds }) {
const user = state.usersObject[id] const user = state.usersObject[id]
user.friendIds = uniq(concat(user.friendIds, friendIds)) user.friendIds = uniq(concat(user.friendIds || [], friendIds))
}, },
saveFollowerIds (state, { id, followerIds }) { saveFollowerIds (state, { id, followerIds }) {
const user = state.usersObject[id] const user = state.usersObject[id]
user.followerIds = uniq(concat(user.followerIds, followerIds)) user.followerIds = uniq(concat(user.followerIds || [], followerIds))
}, },
// Because frontend doesn't have a reason to keep these stuff in memory // Because frontend doesn't have a reason to keep these stuff in memory
// outside of viewing someones user profile. // outside of viewing someones user profile.
@ -202,7 +202,9 @@ export const mutations = {
}, },
setPinnedToUser (state, status) { setPinnedToUser (state, status) {
const user = state.usersObject[status.user.id] const user = state.usersObject[status.user.id]
user.pinnedStatusIds = user.pinnedStatusIds || []
const index = user.pinnedStatusIds.indexOf(status.id) const index = user.pinnedStatusIds.indexOf(status.id)
if (status.pinned && index === -1) { if (status.pinned && index === -1) {
user.pinnedStatusIds.push(status.id) user.pinnedStatusIds.push(status.id)
} else if (!status.pinned && index !== -1) { } else if (!status.pinned && index !== -1) {

View file

@ -2,6 +2,15 @@ import escape from 'escape-html'
import parseLinkHeader from 'parse-link-header' import parseLinkHeader from 'parse-link-header'
import { isStatusNotification } from '../notification_utils/notification_utils.js' import { isStatusNotification } from '../notification_utils/notification_utils.js'
/** NOTICE! **
* Do not initialize UI-generated data here.
* It will override existing data.
*
* i.e. user.pinnedStatusIds was set to [] here
* UI code would update it with data but upon next user fetch
* it would be reverted back to []
*/
const qvitterStatusType = (status) => { const qvitterStatusType = (status) => {
if (status.is_post_verb) { if (status.is_post_verb) {
return 'status' return 'status'
@ -53,7 +62,7 @@ export const parseUser = (data) => {
output.fields = data.fields output.fields = data.fields
output.fields_html = data.fields.map(field => { output.fields_html = data.fields.map(field => {
return { return {
name: addEmojis(field.name, data.emojis), name: addEmojis(escape(field.name), data.emojis),
value: addEmojis(field.value, data.emojis) value: addEmojis(field.value, data.emojis)
} }
}) })
@ -173,9 +182,6 @@ export const parseUser = (data) => {
output.locked = data.locked output.locked = data.locked
output.followers_count = data.followers_count output.followers_count = data.followers_count
output.statuses_count = data.statuses_count output.statuses_count = data.statuses_count
output.friendIds = []
output.followerIds = []
output.pinnedStatusIds = []
if (data.pleroma) { if (data.pleroma) {
output.follow_request_count = data.pleroma.follow_request_count output.follow_request_count = data.pleroma.follow_request_count

View file

@ -0,0 +1,61 @@
import { find } from 'lodash'
const createFaviconService = () => {
let favimg, favcanvas, favcontext, favicon
const faviconWidth = 128
const faviconHeight = 128
const badgeRadius = 32
const initFaviconService = () => {
const nodes = document.getElementsByTagName('link')
favicon = find(nodes, node => node.rel === 'icon')
if (favicon) {
favcanvas = document.createElement('canvas')
favcanvas.width = faviconWidth
favcanvas.height = faviconHeight
favimg = new Image()
favimg.src = favicon.href
favcontext = favcanvas.getContext('2d')
}
}
const isImageLoaded = (img) => img.complete && img.naturalHeight !== 0
const clearFaviconBadge = () => {
if (!favimg || !favcontext || !favicon) return
favcontext.clearRect(0, 0, faviconWidth, faviconHeight)
if (isImageLoaded(favimg)) {
favcontext.drawImage(favimg, 0, 0, favimg.width, favimg.height, 0, 0, faviconWidth, faviconHeight)
}
favicon.href = favcanvas.toDataURL('image/png')
}
const drawFaviconBadge = () => {
if (!favimg || !favcontext || !favcontext) return
clearFaviconBadge()
const style = getComputedStyle(document.body)
const badgeColor = `${style.getPropertyValue('--badgeNotification') || 'rgb(240, 100, 100)'}`
if (isImageLoaded(favimg)) {
favcontext.drawImage(favimg, 0, 0, favimg.width, favimg.height, 0, 0, faviconWidth, faviconHeight)
}
favcontext.fillStyle = badgeColor
favcontext.beginPath()
favcontext.arc(faviconWidth - badgeRadius, badgeRadius, badgeRadius, 0, 2 * Math.PI, false)
favcontext.fill()
favicon.href = favcanvas.toDataURL('image/png')
}
return {
initFaviconService,
clearFaviconBadge,
drawFaviconBadge
}
}
const FaviconService = createFaviconService()
export default FaviconService