@@ -24,6 +24,7 @@ export function show(): void {
2424 beforeAnimation : async ( ) => {
2525 hydrateInputs ( ) ;
2626 originalState = getProfileState ( ) ;
27+ updateSaveButtonState ( ) ;
2728 initializeCharacterCounters ( ) ;
2829 } ,
2930 } ) ;
@@ -33,6 +34,10 @@ function hide(): void {
3334 void modal . hide ( ) ;
3435}
3536
37+ const saveButton = qsr < HTMLButtonElement > (
38+ "#editProfileModal .edit-profile-submit" ,
39+ ) ;
40+
3641const bioInput = qsr < HTMLTextAreaElement > ( "#editProfileModal .bio" ) ;
3742const keyboardInput = qsr < HTMLTextAreaElement > ( "#editProfileModal .keyboard" ) ;
3843const twitterInput = qsr < HTMLInputElement > ( "#editProfileModal .twitter" ) ;
@@ -43,10 +48,41 @@ const showActivityOnPublicProfileInput = qsr<HTMLInputElement>(
4348 "#editProfileModal .editProfileShowActivityOnPublicProfile" ,
4449) ;
4550
51+ bioInput . on ( "input" , ( ) => {
52+ updateSaveButtonState ( ) ;
53+ } ) ;
54+ keyboardInput . on ( "input" , ( ) => {
55+ updateSaveButtonState ( ) ;
56+ } ) ;
57+ twitterInput . on ( "input" , ( ) => {
58+ updateSaveButtonState ( ) ;
59+ } ) ;
60+ githubInput . on ( "input" , ( ) => {
61+ updateSaveButtonState ( ) ;
62+ } ) ;
63+ websiteInput . on ( "input" , ( ) => {
64+ updateSaveButtonState ( ) ;
65+ } ) ;
66+ showActivityOnPublicProfileInput . on ( "change" , ( ) => {
67+ updateSaveButtonState ( ) ;
68+ } ) ;
69+
4670const indicators = [
47- addValidation ( twitterInput , TwitterProfileSchema ) ,
48- addValidation ( githubInput , GithubProfileSchema ) ,
49- addValidation ( websiteInput , WebsiteSchema ) ,
71+ addValidation (
72+ twitterInput ,
73+ TwitterProfileSchema ,
74+ ( ) => originalState ?. twitter ?? "" ,
75+ ) ,
76+ addValidation (
77+ githubInput ,
78+ GithubProfileSchema ,
79+ ( ) => originalState ?. github ?? "" ,
80+ ) ,
81+ addValidation (
82+ websiteInput ,
83+ WebsiteSchema ,
84+ ( ) => originalState ?. website ?? "" ,
85+ ) ,
5086] ;
5187
5288let currentSelectedBadgeId = - 1 ;
@@ -101,6 +137,7 @@ function hydrateInputs(): void {
101137
102138 badgeIdsSelect ?. qsa ( ".badgeSelectionItem" ) ?. removeClass ( "selected" ) ;
103139 ( currentTarget as HTMLElement ) . classList . add ( "selected" ) ;
140+ updateSaveButtonState ( ) ;
104141 } ) ;
105142
106143 indicators . forEach ( ( it ) => it . hide ( ) ) ;
@@ -171,40 +208,33 @@ function hasProfileChanged(
171208 ) ;
172209}
173210
211+ function updateSaveButtonState ( ) : void {
212+ const currentState = getProfileState ( ) ;
213+ const hasChanges = hasProfileChanged ( originalState , currentState ) ;
214+
215+ const hasValidationErrors = [
216+ { value : currentState . twitter , schema : TwitterProfileSchema } ,
217+ { value : currentState . github , schema : GithubProfileSchema } ,
218+ { value : currentState . website , schema : WebsiteSchema } ,
219+ ] . some (
220+ ( { value, schema } ) => value !== "" && ! schema . safeParse ( value ) . success ,
221+ ) ;
222+
223+ saveButton . native . disabled = ! hasChanges || hasValidationErrors ;
224+ }
225+
174226async function updateProfile ( ) : Promise < void > {
175227 const snapshot = DB . getSnapshot ( ) ;
176228 if ( ! snapshot ) return ;
177229
178230 const currentState = getProfileState ( ) ;
231+
179232 if ( ! hasProfileChanged ( originalState , currentState ) ) {
180- showErrorNotification ( "No changes to save" ) ;
233+ updateSaveButtonState ( ) ;
181234 return ;
182235 }
183236
184237 const updates = buildUpdatesFromState ( currentState ) ;
185- // check for length restrictions before sending server requests
186- const githubLengthLimit = 39 ;
187- if (
188- updates . socialProfiles ?. github !== undefined &&
189- updates . socialProfiles ?. github . length > githubLengthLimit
190- ) {
191- showErrorNotification (
192- `GitHub username exceeds maximum allowed length (${ githubLengthLimit } characters).` ,
193- ) ;
194- return ;
195- }
196-
197- const twitterLengthLimit = 20 ;
198- if (
199- updates . socialProfiles ?. twitter !== undefined &&
200- updates . socialProfiles ?. twitter . length > twitterLengthLimit
201- ) {
202- showErrorNotification (
203- `Twitter username exceeds maximum allowed length (${ twitterLengthLimit } characters).` ,
204- ) ;
205- return ;
206- }
207-
208238 showLoaderBar ( ) ;
209239 const response = await Ape . users . updateProfile ( {
210240 body : {
@@ -238,6 +268,7 @@ async function updateProfile(): Promise<void> {
238268function addValidation (
239269 element : ElementWithUtils < HTMLInputElement > ,
240270 schema : Zod . Schema ,
271+ getOriginalValue : ( ) => string ,
241272) : InputIndicator {
242273 const indicator = new InputIndicator ( element , {
243274 valid : {
@@ -257,10 +288,11 @@ function addValidation(
257288
258289 element . on ( "input" , ( event ) => {
259290 const value = ( event . target as HTMLInputElement ) . value ;
260- if ( value === undefined || value === "" ) {
291+ if ( value === undefined || value === "" || value === getOriginalValue ( ) ) {
261292 indicator . hide ( ) ;
262293 return ;
263294 }
295+
264296 const validationResult = schema . safeParse ( value ) ;
265297 if ( ! validationResult . success ) {
266298 indicator . show (
0 commit comments