feat: count characters consistently
This commit is contained in:
		
							parent
							
								
									80ca1cae00
								
							
						
					
					
						commit
						8433a1523a
					
				
					 9 changed files with 54 additions and 20 deletions
				
			
		
							
								
								
									
										11
									
								
								backend/common/common.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								backend/common/common.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| // Package common contains functions and types common to all (or most) packages. | ||||
| package common | ||||
| 
 | ||||
| import "unicode/utf8" | ||||
| 
 | ||||
| func StringLength(s *string) int { | ||||
| 	if s == nil { | ||||
| 		return -1 | ||||
| 	} | ||||
| 	return utf8.RuneCountInString(*s) | ||||
| } | ||||
|  | @ -5,6 +5,7 @@ import ( | |||
| 	"net/http" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/common" | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/db" | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/log" | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/server" | ||||
|  | @ -83,6 +84,25 @@ func (s *Server) createMember(w http.ResponseWriter, r *http.Request) (err error | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if common.StringLength(&cmr.Name) > db.MaxMemberNameLength { | ||||
| 		return server.APIError{ | ||||
| 			Code:    server.ErrBadRequest, | ||||
| 			Details: fmt.Sprintf("Name name too long (max %d, current %d)", db.MaxMemberNameLength, common.StringLength(&cmr.Name)), | ||||
| 		} | ||||
| 	} | ||||
| 	if common.StringLength(cmr.DisplayName) > db.MaxDisplayNameLength { | ||||
| 		return server.APIError{ | ||||
| 			Code:    server.ErrBadRequest, | ||||
| 			Details: fmt.Sprintf("Display name too long (max %d, current %d)", db.MaxDisplayNameLength, common.StringLength(cmr.DisplayName)), | ||||
| 		} | ||||
| 	} | ||||
| 	if common.StringLength(&cmr.Bio) > db.MaxUserBioLength { | ||||
| 		return server.APIError{ | ||||
| 			Code:    server.ErrBadRequest, | ||||
| 			Details: fmt.Sprintf("Bio too long (max %d, current %d)", db.MaxUserBioLength, common.StringLength(&cmr.Bio)), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if err := validateSlicePtr("name", &cmr.Names); err != nil { | ||||
| 		return *err | ||||
| 	} | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ import ( | |||
| 	"net/http" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/common" | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/db" | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/log" | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/server" | ||||
|  | @ -109,22 +110,22 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error { | |||
| 	} | ||||
| 
 | ||||
| 	// validate display name/bio | ||||
| 	if req.Name != nil && len(*req.Name) > db.MaxMemberNameLength { | ||||
| 	if common.StringLength(req.Name) > db.MaxMemberNameLength { | ||||
| 		return server.APIError{ | ||||
| 			Code:    server.ErrBadRequest, | ||||
| 			Details: fmt.Sprintf("Name name too long (max %d, current %d)", db.MaxMemberNameLength, len(*req.Name)), | ||||
| 			Details: fmt.Sprintf("Name name too long (max %d, current %d)", db.MaxMemberNameLength, common.StringLength(req.Name)), | ||||
| 		} | ||||
| 	} | ||||
| 	if req.DisplayName != nil && len(*req.DisplayName) > db.MaxDisplayNameLength { | ||||
| 	if common.StringLength(req.DisplayName) > db.MaxDisplayNameLength { | ||||
| 		return server.APIError{ | ||||
| 			Code:    server.ErrBadRequest, | ||||
| 			Details: fmt.Sprintf("Display name too long (max %d, current %d)", db.MaxDisplayNameLength, len(*req.DisplayName)), | ||||
| 			Details: fmt.Sprintf("Display name too long (max %d, current %d)", db.MaxDisplayNameLength, common.StringLength(req.DisplayName)), | ||||
| 		} | ||||
| 	} | ||||
| 	if req.Bio != nil && len(*req.Bio) > db.MaxUserBioLength { | ||||
| 	if common.StringLength(req.Bio) > db.MaxUserBioLength { | ||||
| 		return server.APIError{ | ||||
| 			Code:    server.ErrBadRequest, | ||||
| 			Details: fmt.Sprintf("Bio too long (max %d, current %d)", db.MaxUserBioLength, len(*req.Bio)), | ||||
| 			Details: fmt.Sprintf("Bio too long (max %d, current %d)", db.MaxUserBioLength, common.StringLength(req.Name)), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import ( | |||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/common" | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/db" | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/log" | ||||
| 	"codeberg.org/u1f320/pronouns.cc/backend/server" | ||||
|  | @ -64,23 +65,22 @@ func (s *Server) patchUser(w http.ResponseWriter, r *http.Request) error { | |||
| 	} | ||||
| 
 | ||||
| 	// validate display name/bio | ||||
| 	if req.DisplayName != nil && len(*req.DisplayName) > db.MaxDisplayNameLength { | ||||
| 	if common.StringLength(req.Username) > db.MaxUsernameLength { | ||||
| 		return server.APIError{ | ||||
| 			Code:    server.ErrBadRequest, | ||||
| 			Details: fmt.Sprintf("Display name too long (max %d, current %d)", db.MaxDisplayNameLength, len(*req.DisplayName)), | ||||
| 			Details: fmt.Sprintf("Name name too long (max %d, current %d)", db.MaxUsernameLength, common.StringLength(req.Username)), | ||||
| 		} | ||||
| 	} | ||||
| 	if req.Bio != nil && len(*req.Bio) > db.MaxUserBioLength { | ||||
| 	if common.StringLength(req.DisplayName) > db.MaxDisplayNameLength { | ||||
| 		return server.APIError{ | ||||
| 			Code:    server.ErrBadRequest, | ||||
| 			Details: fmt.Sprintf("Bio too long (max %d, current %d)", db.MaxUserBioLength, len(*req.Bio)), | ||||
| 			Details: fmt.Sprintf("Display name too long (max %d, current %d)", db.MaxDisplayNameLength, common.StringLength(req.DisplayName)), | ||||
| 		} | ||||
| 	} | ||||
| 	// this is considered a name | ||||
| 	if req.MemberTitle != nil && len(*req.MemberTitle) > db.MaxDisplayNameLength { | ||||
| 	if common.StringLength(req.Bio) > db.MaxUserBioLength { | ||||
| 		return server.APIError{ | ||||
| 			Code:    server.ErrBadRequest, | ||||
| 			Details: fmt.Sprintf("Member title too long (max %d, current %d)", db.MaxDisplayNameLength, len(*req.MemberTitle)), | ||||
| 			Details: fmt.Sprintf("Bio too long (max %d, current %d)", db.MaxUserBioLength, common.StringLength(req.Bio)), | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,6 +6,8 @@ const md = new MarkdownIt({ | |||
|   breaks: true, | ||||
| }).disable(["heading", "link", "table"]); | ||||
| 
 | ||||
| export default function renderMarkdown(src: string | null) { | ||||
| export function renderMarkdown(src: string | null) { | ||||
|   return src ? sanitize(md.render(src)) : null; | ||||
| } | ||||
| 
 | ||||
| export const charCount = (str: string) => [...str].length; | ||||
|  | @ -30,7 +30,7 @@ | |||
|   import { apiFetchClient } from "$lib/api/fetch"; | ||||
|   import ErrorAlert from "$lib/components/ErrorAlert.svelte"; | ||||
|   import { goto } from "$app/navigation"; | ||||
|   import renderMarkdown from "$lib/api/markdown"; | ||||
|   import {renderMarkdown} from "$lib/utils" | ||||
|   import ReportButton from "./ReportButton.svelte"; | ||||
|   import ProfileLink from "./ProfileLink.svelte"; | ||||
|   import { memberNameRegex } from "$lib/api/regex"; | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ | |||
|   import { memberAvatars, pronounDisplay, WordStatus } from "$lib/api/entities"; | ||||
|   import { PUBLIC_BASE_URL } from "$env/static/public"; | ||||
|   import { userStore } from "$lib/store"; | ||||
|   import renderMarkdown from "$lib/api/markdown"; | ||||
|   import { renderMarkdown } from "$lib/utils"; | ||||
|   import ReportButton from "../ReportButton.svelte"; | ||||
|   import ProfileLink from "../ProfileLink.svelte"; | ||||
|   import StatusLine from "$lib/components/StatusLine.svelte"; | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ | |||
|   import type { PageData } from "./$types"; | ||||
|   import { addToast, delToast } from "$lib/toast"; | ||||
|   import { memberNameRegex } from "$lib/api/regex"; | ||||
|   import renderMarkdown from "$lib/api/markdown"; | ||||
|   import { charCount, renderMarkdown } from "$lib/utils"; | ||||
| 
 | ||||
|   const MAX_AVATAR_BYTES = 1_000_000; | ||||
| 
 | ||||
|  | @ -445,7 +445,7 @@ | |||
|         <textarea class="form-control" style="height: 200px;" bind:value={bio} /> | ||||
|       </div> | ||||
|       <p class="text-muted mt-1"> | ||||
|         Using {bio.length}/{MAX_DESCRIPTION_LENGTH} characters | ||||
|         Using {charCount(bio)}/{MAX_DESCRIPTION_LENGTH} characters | ||||
|       </p> | ||||
|       <p class="text-muted my-2"> | ||||
|         <Icon name="info-circle-fill" aria-hidden /> Your bio supports limited | ||||
|  |  | |||
|  | @ -34,7 +34,7 @@ | |||
|   import ErrorAlert from "$lib/components/ErrorAlert.svelte"; | ||||
|   import { addToast, delToast } from "$lib/toast"; | ||||
|   import type { PageData } from "./$types"; | ||||
|   import renderMarkdown from "$lib/api/markdown"; | ||||
|   import { charCount, renderMarkdown } from "$lib/utils"; | ||||
| 
 | ||||
|   const MAX_AVATAR_BYTES = 1_000_000; | ||||
| 
 | ||||
|  | @ -373,7 +373,7 @@ | |||
|         <textarea class="form-control" style="height: 200px;" bind:value={bio} /> | ||||
|       </div> | ||||
|       <p class="text-muted mt-1"> | ||||
|         Using {bio.length}/{MAX_DESCRIPTION_LENGTH} characters | ||||
|         Using {charCount(bio)}/{MAX_DESCRIPTION_LENGTH} characters | ||||
|       </p> | ||||
|       <p class="text-muted my-2"> | ||||
|         <Icon name="info-circle-fill" aria-hidden /> Your bio supports limited | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue