function HookSortButton( sortButton, achievementUpdates, oldAchievementRows, CreateAchievementRow, isCompareView )
{
	sortButton.addEventListener( 'click', ( /** @type {MouseEvent} */ e ) =>
	{
		e.preventDefault();

		sortButton.setAttribute( 'disabled', 'true' );

		// Request the same page in finnish because it has an easier date format to parse
		const url = new URL( window.location.href );
		url.searchParams.set( 'l', 'finnish' );

		fetch( url, {
			headers: {
				Accept: 'text/html',
				'X-Requested-With': 'SteamDB',
			},
		} )
			.then( response => response.text() )
			.then( text =>
			{
				const parser = new DOMParser();
				const htmlDocument = parser.parseFromString( text, 'text/html' );
				const otherAchievementRows = htmlDocument.querySelectorAll( '#personalAchieve .achieveRow' );

				if( otherAchievementRows.length !== oldAchievementRows.length )
				{
					throw new Error( 'Mismatching amount of achievements' );
				}

				const currentYear = new Date().getFullYear();

				/** @type {Record<number, AchievementData>} */
				const achievementDataMap = [];

				for( const update of achievementUpdates )
				{
					for( const achievement of update.achievementData )
					{
						if( achievement.domId === null )
						{
							continue;
						}

						achievementDataMap[ achievement.domId ] = achievement;
					}
				}

				/** @type {AchievementData[][]} */
				const unlockedAchievementsPerUpdate = [];

				for( let updateId = 0; updateId < achievementUpdates.length; updateId++ )
				{
					unlockedAchievementsPerUpdate[ updateId ] = [];
				}

				// Assume the achievements are in the same order
				for( let id = 0; id < otherAchievementRows.length; id++ )
				{
					const otherAchievement = otherAchievementRows[ id ];
					const oldAchievement = oldAchievementRows[ id ];

					/** @type {HTMLImageElement} */
					const otherImage = otherAchievement.querySelector( '.achieveImgHolder > img' );

					/** @type {HTMLImageElement} */
					const oldImage = oldAchievement.querySelector( '.achieveImgHolder > img' );

					if( !otherImage || !oldImage )
					{
						continue;
					}

					// Verify that we are seeing the same achievements by their image at least
					if( otherImage.src !== oldImage.src )
					{
						throw new Error( 'Mismatching achievement icon' );
					}

					const unlock = isCompareView
						? otherAchievement.querySelector( '.achieveUnlockTime' )
							?.childNodes[ 0 ].textContent.trim()
						: otherAchievement.querySelector( '.achieveUnlockTime' )
							?.textContent.trim();

					if( !unlock )
					{
						continue;
					}

					const parsedTime = unlock.match( /Avattu (?<day>[0-9]+)\.(?<month>[0-9]+)\.(?<year>[0-9]+)? klo (?<hour>[0-9]+)\.(?<minute>[0-9]+)/ );

					if( !parsedTime )
					{
						throw new Error( 'Failed to parse unlock time' );
					}

					const c = parsedTime.groups;
					const date = new Date(
						c.year ? Number.parseInt( c.year, 10 ) : currentYear,
						Number.parseInt( c.month, 10 ) - 1,
						Number.parseInt( c.day, 10 ),
						Number.parseInt( c.hour, 10 ),
						Number.parseInt( c.minute, 10 ),
						0,
						0
					);

					const achievement = achievementDataMap[ id ];
					achievement.player.unlockTimestamp = date.getTime();
					unlockedAchievementsPerUpdate[ achievement.update ].push( achievement );
				}

				// Redraw earned achievements block with new sorting
				const RedrawSortedAchievements = () =>
				{
					sortButton.remove();

					for( let updateId = 0; updateId < achievementUpdates.length; updateId++ )
					{
						const update = achievementUpdates[ updateId ];
						const unlockedAchievements = unlockedAchievementsPerUpdate[ updateId ];

						if( !update.earnedDetailsElement )
						{
							continue;
						}

						while( update.earnedDetailsElement.lastElementChild.tagName !== 'SUMMARY' )
						{
							update.earnedDetailsElement.lastElementChild.remove();
						}

						unlockedAchievements.sort( ( a, b ) =>
						{
							const aTime = a.player.unlockTimestamp;
							const bTime = b.player.unlockTimestamp;

							if( aTime === bTime )
							{
								return a.id - b.id;
							}

							return bTime > aTime ? 1 : -1;
						} );

						for( const achievement of unlockedAchievements )
						{
							const element = CreateAchievementRow( achievement );
							update.earnedDetailsElement.append( element );
						}
					}
				};

				StartViewTransition( RedrawSortedAchievements );
			} )
			.catch( e =>
			{
				WriteLog( e );
				alert( `Failed to sort achievements: ${e.message}` );
			} );
	}, { once: true } );
}
@param {HTMLButtonElement} sortButton
@param {AchievementGroup[]} achievementUpdates
@param {NodeListOf<HTMLElement>} oldAchievementRows
@param {(data: AchievementData) => HTMLDivElement} CreateAchievementRow
@param {boolean} isCompareView