add option to remove emoji from user display names (#450)
* add option to remove emoji from user display names fixes #449 * slight memory perf improvement
This commit is contained in:
		
							parent
							
								
									350667e5df
								
							
						
					
					
						commit
						37e12e8d73
					
				
					 9 changed files with 92 additions and 5 deletions
				
			
		
							
								
								
									
										5
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							|  | @ -4177,6 +4177,11 @@ | |||
|         "minimalistic-crypto-utils": "^1.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "emoji-regex": { | ||||
|       "version": "7.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.0.tgz", | ||||
|       "integrity": "sha512-lnvttkzAlYW8WpFPiStPWyd/YdS02cFsYwXwWqnbKY43fMgUeUx+vzW1Zaozu34n4Fm7sxygi8+SEL6dcks/hQ==" | ||||
|     }, | ||||
|     "emojis-list": { | ||||
|       "version": "2.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", | ||||
|  |  | |||
|  | @ -55,6 +55,7 @@ | |||
|     "chokidar": "^2.0.4", | ||||
|     "cross-env": "^5.2.0", | ||||
|     "css-loader": "^1.0.0", | ||||
|     "emoji-regex": "^7.0.0", | ||||
|     "escape-html": "^1.0.3", | ||||
|     "esm": "^3.0.77", | ||||
|     "events": "^3.0.0", | ||||
|  |  | |||
|  | @ -5,17 +5,29 @@ | |||
|   } | ||||
| </style> | ||||
| <script> | ||||
|   import { emojifyText } from '../../_utils/emojifyText' | ||||
|   import { emojifyText, removeEmoji } from '../../_utils/emojifyText' | ||||
|   import { store } from '../../_store/store' | ||||
|   import escapeHtml from 'escape-html' | ||||
|   import emojiRegex from 'emoji-regex' | ||||
| 
 | ||||
|   let theEmojiRegex | ||||
| 
 | ||||
|   export default { | ||||
|     store: () => store, | ||||
|     computed: { | ||||
|       emojis: ({ account }) => (account.emojis || []), | ||||
|       accountName: ({ account }) => (account.display_name || account.username), | ||||
|       massagedAccountName: ({ accountName, emojis, $autoplayGifs }) => { | ||||
|       massagedAccountName: ({ accountName, emojis, $autoplayGifs, $omitEmojiInDisplayNames }) => { | ||||
|         accountName = escapeHtml(accountName) | ||||
| 
 | ||||
|         if ($omitEmojiInDisplayNames) { // display name emoji are annoying to some screenreader users | ||||
|           theEmojiRegex = theEmojiRegex || emojiRegex() // only init when needed | ||||
|           let emojiFreeAccountName = removeEmoji(accountName.replace(theEmojiRegex, ''), emojis).trim() | ||||
|           if (emojiFreeAccountName) { | ||||
|             return emojiFreeAccountName // only remove emoji if the resulting username is non-empty | ||||
|           } | ||||
|         } | ||||
| 
 | ||||
|         return emojifyText(accountName, emojis, $autoplayGifs) | ||||
|       } | ||||
|     } | ||||
|  |  | |||
|  | @ -18,6 +18,11 @@ | |||
|              bind:checked="$reduceMotion" on:change="$save()"> | ||||
|       <label for="choice-reduce-motion">Reduce motion in UI animations</label> | ||||
|     </div> | ||||
|     <div class="setting-group"> | ||||
|       <input type="checkbox" id="choice-omit-emoji-in-display-names" | ||||
|              bind:checked="$omitEmojiInDisplayNames" on:change="$save()"> | ||||
|       <label for="choice-omit-emoji-in-display-names">Remove emoji from user display names</label> | ||||
|     </div> | ||||
|   </form> | ||||
| 
 | ||||
| </SettingsLayout> | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ const KEYS_TO_STORE_IN_LOCAL_STORAGE = new Set([ | |||
|   'autoplayGifs', | ||||
|   'markMediaAsSensitive', | ||||
|   'reduceMotion', | ||||
|   'omitEmojiInDisplayNames', | ||||
|   'pinnedPages', | ||||
|   'composeData' | ||||
| ]) | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import { replaceAll } from './strings' | ||||
| 
 | ||||
| export function emojifyText (text, emojis, autoplayGifs) { | ||||
|   if (emojis && emojis.length) { | ||||
|   if (emojis) { | ||||
|     for (let emoji of emojis) { | ||||
|       let urlToUse = autoplayGifs ? emoji.url : emoji.static_url | ||||
|       let shortcodeWithColons = `:${emoji.shortcode}:` | ||||
|  | @ -15,3 +15,13 @@ export function emojifyText (text, emojis, autoplayGifs) { | |||
|   } | ||||
|   return text | ||||
| } | ||||
| 
 | ||||
| export function removeEmoji (text, emojis) { | ||||
|   if (emojis) { | ||||
|     for (let emoji of emojis) { | ||||
|       let shortcodeWithColons = `:${emoji.shortcode}:` | ||||
|       text = replaceAll(text, shortcodeWithColons, '') | ||||
|     } | ||||
|   } | ||||
|   return text | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| export function replaceAll (string, replacee, replacement) { | ||||
|   if (!string.length || !replacee.length || !replacement.length) { | ||||
|   if (!string.length || !replacee.length) { | ||||
|     return string | ||||
|   } | ||||
|   let idx | ||||
|  |  | |||
|  | @ -1,5 +1,10 @@ | |||
| import { loginAsFoobar } from '../roles' | ||||
| import { displayNameInComposeBox, getNthStatusSelector, getUrl, sleep } from '../utils' | ||||
| import { | ||||
|   displayNameInComposeBox, generalSettingsButton, getNthStatusSelector, getUrl, homeNavButton, | ||||
|   removeEmojiFromDisplayNamesInput, | ||||
|   settingsNavButton, | ||||
|   sleep | ||||
| } from '../utils' | ||||
| import { updateUserDisplayNameAs } from '../serverActions' | ||||
| import { Selector as $ } from 'testcafe' | ||||
| 
 | ||||
|  | @ -25,3 +30,49 @@ test('Cannot XSS using display name HTML', async t => { | |||
|   await t | ||||
|     .expect(displayNameInComposeBox.innerText).eql('<script>alert("pwn")</script>') | ||||
| }) | ||||
| 
 | ||||
| test('Can remove emoji from user display names', async t => { | ||||
|   await updateUserDisplayNameAs('foobar', '🌈 foo :blobpats: 🌈') | ||||
|   await sleep(1000) | ||||
|   await loginAsFoobar(t) | ||||
|   await t | ||||
|     .expect(displayNameInComposeBox.innerText).eql('🌈 foo  🌈') | ||||
|     .expect($('.compose-box-display-name img').exists).ok() | ||||
|     .click(settingsNavButton) | ||||
|     .click(generalSettingsButton) | ||||
|     .click(removeEmojiFromDisplayNamesInput) | ||||
|     .expect(removeEmojiFromDisplayNamesInput.checked).ok() | ||||
|     .click(homeNavButton) | ||||
|     .expect(displayNameInComposeBox.innerText).eql('foo') | ||||
|     .expect($('.compose-box-display-name img').exists).notOk() | ||||
|     .click(settingsNavButton) | ||||
|     .click(generalSettingsButton) | ||||
|     .click(removeEmojiFromDisplayNamesInput) | ||||
|     .expect(removeEmojiFromDisplayNamesInput.checked).notOk() | ||||
|     .click(homeNavButton) | ||||
|     .expect(displayNameInComposeBox.innerText).eql('🌈 foo  🌈') | ||||
|     .expect($('.compose-box-display-name img').exists).ok() | ||||
| }) | ||||
| 
 | ||||
| test('Cannot remove emoji from user display names if result would be empty', async t => { | ||||
|   await updateUserDisplayNameAs('foobar', '🌈 :blobpats: 🌈') | ||||
|   await sleep(1000) | ||||
|   await loginAsFoobar(t) | ||||
|   await t | ||||
|     .expect(displayNameInComposeBox.innerText).eql('🌈  🌈') | ||||
|     .expect($('.compose-box-display-name img').exists).ok() | ||||
|     .click(settingsNavButton) | ||||
|     .click(generalSettingsButton) | ||||
|     .click(removeEmojiFromDisplayNamesInput) | ||||
|     .expect(removeEmojiFromDisplayNamesInput.checked).ok() | ||||
|     .click(homeNavButton) | ||||
|     .expect(displayNameInComposeBox.innerText).eql('🌈  🌈') | ||||
|     .expect($('.compose-box-display-name img').exists).ok() | ||||
|     .click(settingsNavButton) | ||||
|     .click(generalSettingsButton) | ||||
|     .click(removeEmojiFromDisplayNamesInput) | ||||
|     .expect(removeEmojiFromDisplayNamesInput.checked).notOk() | ||||
|     .click(homeNavButton) | ||||
|     .expect(displayNameInComposeBox.innerText).eql('🌈  🌈') | ||||
|     .expect($('.compose-box-display-name img').exists).ok() | ||||
| }) | ||||
|  |  | |||
|  | @ -41,6 +41,8 @@ export const followsButton = $('.account-profile-details > *:nth-child(2)') | |||
| export const followersButton = $('.account-profile-details > *:nth-child(3)') | ||||
| export const avatarInComposeBox = $('.compose-box-avatar') | ||||
| export const displayNameInComposeBox = $('.compose-box-display-name') | ||||
| export const generalSettingsButton = $('a[href="/settings/general"]') | ||||
| export const removeEmojiFromDisplayNamesInput = $('#choice-omit-emoji-in-display-names') | ||||
| 
 | ||||
| export const favoritesCountElement = $('.status-favs-reblogs:nth-child(3)').addCustomDOMProperties({ | ||||
|   innerCount: el => parseInt(el.innerText, 10) | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue