nextcloud-vue icon indicating copy to clipboard operation
nextcloud-vue copied to clipboard

Avatar placeholder should not take special characters

Open skjnldsv opened this issue 3 years ago • 10 comments

image

skjnldsv avatar Jun 20 '21 07:06 skjnldsv

Can I give it a try? If so, I will create a PR to edit Avatar.vue.

aloisdg avatar Oct 07 '21 13:10 aloisdg

@aloisdg go for it :)

skjnldsv avatar Oct 26 '21 12:10 skjnldsv

Hi!

Here is some testcases I would like to check with you:

input before after
? ?
A A A
B B B
a a A
b b B
1 1 1
( ( ?
Foo F F
(Foo ( F
_Foo _ F
Foo Bar FB FB
foo bar FB FB
foo a bar FA FA
foo _ bar F_ FB
foo _bar F_ FB

before is the behavior of the current code, after is my current implementation. Is it right for you?

Open to see the code
// https://stackoverflow.com/a/25352300/1248177
const isAlphaNumeric = (str) => {
  for (let i = 0; i < str.length; i++) {
    const code = str.charCodeAt(i);
    if (!(code > 47 && code < 58) && // numeric (0-9)
      !(code > 64 && code < 91) && // upper alpha (A-Z)
      !(code > 96 && code < 123)) { // lower alpha (a-z)
      return false;
    }
  }
  return true;
};

const filterAlphaNumerics = (word) => [...word].filter(isAlphaNumeric);

const filterWords = (items) => items.split(' ').map(filterAlphaNumerics).filter(w => w.length > 0);

const extractInitials = (getUserIdentifier, shouldShowPlaceholder) => {
  if (!shouldShowPlaceholder)
    return undefined;
  if (!getUserIdentifier)
    return '?';
  const words = filterWords(getUserIdentifier);
  switch (words.length) {
    case 0:
      return "?";
    case 1:
      return words[0][0].toUpperCase();
    default:
      return (words[0][0] + words[1][0]).toUpperCase()
  }
}

Note that this is a WIP.

~~Try it Online~~ Demo

Use it by replacing https://github.com/nextcloud/nextcloud-vue/blob/b5339a584ba62114f40635dfa1b9db4f346b421d/src/components/Avatar/Avatar.vue#L365 with

initials() { return extractInitials(this.getUserIdentifier, this.shouldShowPlaceholder); },

aloisdg avatar Nov 05 '21 12:11 aloisdg

Hi, Is this still open ?

ak090498 avatar Sep 16 '22 06:09 ak090498

@knowasaritra I didn't have any comment on my code. What do you think of it?

aloisdg avatar Sep 17 '22 10:09 aloisdg

@aloisdg won't it better if we use regex , the way you are handling isAlphaNumeric function can be modified with regex I guess ... I am still a beginner I can be wrong.

ak090498 avatar Sep 17 '22 20:09 ak090498

@knownasaritra from the stack overflow link:

str.charCodeAt(i) appears to be faster than the regular expression alternative. In my test on jsPerf the RegExp option performs 66% slower in Chrome 36 (and slightly slower in Firefox 31).

one thing we could do though is to simplify this expression a bit:

const isAlphaNumeric = (str) => {
  for (let i = 0; i < str.length; i++) {
    const code = str.charCodeAt(i);
    if (!(code > 47 && code < 58) && // numeric (0-9)
      !(code > 64 && code < 91) && // upper alpha (A-Z)
      !(code > 96 && code < 123)) { // lower alpha (a-z)
      return false;
    }
  }
  return true;
};

here what I got:

const isNumeric = code => code > 47 && code < 58;
const isUpperAlpha = code => code > 64 && code < 91;
const isLowerAlpha = code => code > 96 && code < 123;
const isLetterAlphaNumeric = code => isNumeric(code) || isUpperAlpha(code) || isLowerAlpha(code);

const isAlphaNumeric = (str) => Array.from(str)
	.map((_, i) => str.charCodeAt(i))
	.every(isLetterAlphaNumeric);

or

const isCodeAlphaNumeric = c => (c > 47 && c < 58) || (c > 64 && c < 91) || (c > 96 && c < 123);

const isAlphaNumeric = (str) => Array.from(str)
	.map((_, i) => str.charCodeAt(i))
	.every(isCodeAlphaNumeric);

note: isalnum IsCharAlphaNumericA

aloisdg avatar Sep 17 '22 22:09 aloisdg

One more thing, what should we do with non latin character like "わ"?

aloisdg avatar Sep 18 '22 00:09 aloisdg

hmm.....that will be a lot of work , if we consider every single possibility, because checks need to be made for every single non-latin languages.

ak090498 avatar Sep 18 '22 07:09 ak090498

Alright then. I guess my fix should do the trick.

aloisdg avatar Oct 07 '22 13:10 aloisdg

@aloisdg thanks for diving into this. How would that work with foreign characters like Chinese, Japanese, Korean or Cyrillic ? It seems you could easily do that with regex. This library seems interesting: https://github.com/regexhq/word-regex See testing being done here https://github.com/regexhq/word-regex/blob/master/test.js

Rough example: https://codepen.io/skjnldsv/pen/oNdRVPj?editors=1010

skjnldsv avatar Oct 19 '22 11:10 skjnldsv

No need for special library, ES2015 supports unicode classes:

const input = 'your string'
filtered = input.match(/[\p{L}\p{N}\s]/gu).join('')
  • \p{L}: Letters of all languages
  • \p{N}: Numbers of all languages
  • \s: White space for breaking the string

susnux avatar Jun 15 '23 16:06 susnux