Initial Release
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
(() => {
|
||||
const
|
||||
_Profile = $('.profile-wrapper'),
|
||||
_Preview = $('.profile-container'),
|
||||
_Status = $('#profile-status'),
|
||||
// Option Elements
|
||||
iDisplayname = $('#input-displayname'),
|
||||
iSubtitle = $('#input-subtitle'),
|
||||
iBio = $('#input-bio'),
|
||||
iAvatarFile = $('#input-avatar-file'),
|
||||
iBannerFile = $('#input-banner-file'),
|
||||
iBannerColor = $('#input-banner'),
|
||||
iBorderColor = $('#input-border'),
|
||||
iBackgroundColor = $('#input-background'),
|
||||
iSubmit = $('#button-submit'),
|
||||
iCancel = $('#button-cancel'),
|
||||
// Preview Elements
|
||||
pBanner = $('#profile-banner'),
|
||||
pAvatar = $('#profile-avatar'),
|
||||
pBadges = $('#profile-badges'),
|
||||
pDisplayname = $('#profile-displayname'),
|
||||
pUsername = $('#profile-username'),
|
||||
pSubtitle = $('#profile-subtitle'),
|
||||
pDivider = $('#profile-divider'),
|
||||
pBio = $('#profile-bio'),
|
||||
// Buttons
|
||||
bAvatar = $('#button-avatar-remove'),
|
||||
bBanner = $('#button-banner-remove')
|
||||
|
||||
// Elements that the user interacts with in real time
|
||||
const _InputElements = [
|
||||
iDisplayname,
|
||||
iSubtitle,
|
||||
iBio,
|
||||
iBannerColor,
|
||||
iBorderColor,
|
||||
iBackgroundColor,
|
||||
// These elements have async calls
|
||||
// they'll update the preview when complete
|
||||
// pBanner,
|
||||
// pAvatar,
|
||||
]
|
||||
const _RecognizedBadges = [
|
||||
[1 << 0, 'crown', 'The Boss'],
|
||||
[1 << 1, 'star', 'V.I.P'],
|
||||
[1 << 2, 'picross', 'Picross Master'],
|
||||
]
|
||||
|
||||
SendRequest('GET', '/api/v1/users/@me', null, 1)
|
||||
.then(([_, body]) => {
|
||||
let removingBanner = false
|
||||
let removingAvatar = false
|
||||
let unsavedChanges = false
|
||||
const hexToDec = e => parseInt(e.value.slice(1), 16)
|
||||
const trimDataURL = (s, x = undefined) => s.slice(s.indexOf(',') + 1, x)
|
||||
const decToHex = (e, x) => '#' + (e?.toString(16).padStart('6', 0) || x)
|
||||
|
||||
function FormReset() {
|
||||
const colorBanner = decToHex(body.accent_banner, '1d1e2e')
|
||||
const colorBorder = decToHex(body.accent_border, '282a3f')
|
||||
const colorBackground = decToHex(body.accent_background, '333550')
|
||||
|
||||
// Reset Options
|
||||
iDisplayname.value = body.displayname
|
||||
iSubtitle.value = body.subtitle
|
||||
iBio.value = body.biography
|
||||
iBannerColor.value = colorBanner
|
||||
iBorderColor.value = colorBorder
|
||||
iBackgroundColor.value = colorBackground
|
||||
iAvatarFile.value = null
|
||||
iBannerFile.value = null
|
||||
|
||||
// Reset Preview
|
||||
pDisplayname.textContent = body.displayname
|
||||
pUsername.textContent = '@' + body.username
|
||||
pSubtitle.textContent = body.subtitle
|
||||
pBio.textContent = body.biography
|
||||
body.biography
|
||||
? pDivider.removeAttribute('hidden')
|
||||
: pDivider.setAttribute('hidden', true)
|
||||
pAvatar.src = ImageURL('avatars', body.id, body.avatar, true, 128)
|
||||
pBanner.style.backgroundImage = body.banner
|
||||
? `url('${ImageURL('banners', body.id, body.banner, true, 512)}')`
|
||||
: ''
|
||||
_Profile.style.setProperty('--accent-banner', colorBanner)
|
||||
_Profile.style.setProperty('--accent-border', colorBorder)
|
||||
_Profile.style.setProperty('--accent-background', colorBackground)
|
||||
iBackgroundColor.oninput()
|
||||
|
||||
// Disable Buttons
|
||||
removingAvatar = false
|
||||
removingBanner = false
|
||||
FormUpdated()
|
||||
}
|
||||
function FormUpdated() {
|
||||
unsavedChanges = (Object.entries(CollectBody()).length > 0)
|
||||
if (unsavedChanges) {
|
||||
iCancel.removeAttribute('disabled')
|
||||
iSubmit.removeAttribute('disabled')
|
||||
} else {
|
||||
iCancel.setAttribute('disabled', true)
|
||||
iSubmit.setAttribute('disabled', true)
|
||||
}
|
||||
!removingBanner && (body.banner !== null || iBannerFile.files.length > 0)
|
||||
? bBanner.removeAttribute('disabled')
|
||||
: bBanner.setAttribute('disabled', true)
|
||||
|
||||
!removingAvatar && (body.avatar !== null || iAvatarFile.files.length > 0)
|
||||
? bAvatar.removeAttribute('disabled')
|
||||
: bAvatar.setAttribute('disabled', true)
|
||||
}
|
||||
function CollectBody() {
|
||||
const data = {}
|
||||
// Collect Textboxes
|
||||
if ((iDisplayname.value.trim() || body.displayname) !== body.displayname)
|
||||
data['displayname'] = iDisplayname.value
|
||||
if (iBio.value.trim() !== (body.biography || ''))
|
||||
data['biography'] = iBio.value
|
||||
if (iSubtitle.value.trim() !== (body.subtitle || ''))
|
||||
data['subtitle'] = iSubtitle.value
|
||||
|
||||
// Collect Colors
|
||||
const decBannerColor = hexToDec(iBannerColor)
|
||||
if (decBannerColor !== (body.accent_banner || 1908270))
|
||||
data['accent_banner'] = decBannerColor
|
||||
const decBorderColor = hexToDec(iBorderColor)
|
||||
if (decBorderColor !== (body.accent_border || 2632255))
|
||||
data['accent_border'] = decBorderColor
|
||||
const decBackgroundColor = hexToDec(iBackgroundColor)
|
||||
if (decBackgroundColor !== (body.accent_background || 3355984))
|
||||
data['accent_background'] = decBackgroundColor
|
||||
|
||||
// Collect Images
|
||||
if (removingAvatar && body.avatar) data['avatar'] = ''
|
||||
if (removingBanner && body.banner) data['banner'] = ''
|
||||
if (iAvatarFile.files.length)
|
||||
data['avatar'] = trimDataURL(pAvatar.src)
|
||||
if (iBannerFile.files.length)
|
||||
data['banner'] = trimDataURL(pBanner.style.backgroundImage, -2)
|
||||
|
||||
console.log(data)
|
||||
return data
|
||||
}
|
||||
|
||||
// Templating
|
||||
_InputElements.forEach(e => e.addEventListener('change', FormUpdated))
|
||||
_RecognizedBadges.forEach(b => {
|
||||
if ((body.public_flags & b[0]) !== 0) {
|
||||
const img = document.createElement('img')
|
||||
img.src = `/assets/images/icons/badge-${b[1]}.svg`
|
||||
img.title = b[2]
|
||||
img.alt = b[2]
|
||||
pBadges.appendChild(img)
|
||||
}
|
||||
})
|
||||
|
||||
// Section: About Me
|
||||
iDisplayname.oninput = () => pDisplayname.textContent = (iDisplayname.value || body.displayname).trim()
|
||||
iSubtitle.oninput = () => pSubtitle.textContent = iSubtitle.value.trim()
|
||||
iBio.oninput = () => {
|
||||
const content = (iBio.value || '')
|
||||
pBio.textContent = content.trim()
|
||||
content.length > 0
|
||||
? pDivider.removeAttribute('hidden')
|
||||
: pDivider.setAttribute('hidden', true)
|
||||
}
|
||||
|
||||
// Section: Images
|
||||
$('#button-avatar').onclick = () => iAvatarFile.click()
|
||||
$('#button-banner').onclick = () => iBannerFile.click()
|
||||
bAvatar.onclick = () => {
|
||||
pAvatar.src = ImageURL('avatars', body.id, null)
|
||||
iAvatarFile.value = null
|
||||
removingAvatar = true
|
||||
FormUpdated()
|
||||
}
|
||||
bBanner.onclick = () => {
|
||||
pBanner.style.backgroundImage = null
|
||||
iBannerFile.value = null
|
||||
removingBanner = true
|
||||
FormUpdated()
|
||||
}
|
||||
|
||||
iAvatarFile.onchange = (ev) => {
|
||||
const file = ev.target.files[0]
|
||||
if (!file) return
|
||||
const reader = new FileReader()
|
||||
reader.onload = e => {
|
||||
pAvatar.src = e.target.result
|
||||
removingAvatar = false
|
||||
FormUpdated()
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
iBannerFile.onchange = (ev) => {
|
||||
const file = ev.target.files[0]
|
||||
if (!file) return
|
||||
const reader = new FileReader()
|
||||
reader.onload = e => {
|
||||
pBanner.style.backgroundImage = `url(${e.target.result})`
|
||||
removingBanner = false
|
||||
FormUpdated()
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
|
||||
// Section: Accents
|
||||
iBannerColor.oninput = () => _Profile.style.setProperty('--accent-banner', iBannerColor.value)
|
||||
iBorderColor.oninput = () => _Profile.style.setProperty('--accent-border', iBorderColor.value)
|
||||
iBackgroundColor.oninput = () => {
|
||||
let hexColor = iBackgroundColor.value
|
||||
_Profile.style.setProperty('--accent-background', hexColor)
|
||||
|
||||
// Check Luminance of Background Color
|
||||
hexColor = hexColor.replace('#', '')
|
||||
var r = parseInt(hexColor.substr(0, 2), 16)
|
||||
var g = parseInt(hexColor.substr(2, 2), 16)
|
||||
var b = parseInt(hexColor.substr(4, 2), 16)
|
||||
if (((0.299 * r) + (0.587 * g) + (0.114 * b)) / 255 > 0.85) {
|
||||
_Preview.classList.add('profile-dark')
|
||||
} else {
|
||||
_Preview.classList.remove('profile-dark')
|
||||
}
|
||||
}
|
||||
|
||||
// Section: Actions
|
||||
iCancel.onclick = () => FormReset()
|
||||
iSubmit.onclick = () => {
|
||||
if (!unsavedChanges) return
|
||||
const unlock = LockInteraction()
|
||||
_Status.textContent = 'Saving Changes...'
|
||||
SendRequest('PATCH', '/api/v1/users/@me', CollectBody(), 1)
|
||||
.then(([_, patchedBody]) => {
|
||||
_Status.textContent = 'Profile Saved!'
|
||||
_Status.classList.remove('text-error')
|
||||
localStorage.removeItem('cache_home_me') // Used by page-home.js
|
||||
body = patchedBody
|
||||
unlock()
|
||||
FormReset()
|
||||
})
|
||||
.catch(e => {
|
||||
_Status.textContent = e
|
||||
_Status.classList.add('text-error')
|
||||
unlock()
|
||||
})
|
||||
// we dont use finally block here because race condition :p
|
||||
}
|
||||
|
||||
// Display Profile
|
||||
_Profile.removeAttribute('hidden')
|
||||
FormReset()
|
||||
})
|
||||
.catch(e => {
|
||||
// Display Error Message
|
||||
$('#alert-error').removeAttribute('hidden')
|
||||
$('#error-message').textContent = e
|
||||
console.log(e)
|
||||
})
|
||||
.finally(() => {
|
||||
// Hide Loading Symbol
|
||||
$('#alert-load').setAttribute('hidden', true)
|
||||
})
|
||||
})()
|
||||
Reference in New Issue
Block a user