Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement account functions #34

Merged
merged 34 commits into from
Jul 14, 2024
Merged
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4976505
add basic account page style
cbh778899 Jul 11, 2024
a6c619e
scroll to bottom when switch session
cbh778899 Jul 11, 2024
c4b817b
rename type: out->user; in->assistant
cbh778899 Jul 11, 2024
126a931
add functions to manage cookies
cbh778899 Jul 12, 2024
68aef6c
use different session title
cbh778899 Jul 12, 2024
2a1d1e0
implement user page
cbh778899 Jul 12, 2024
3997d58
add custom hook to manage user information
cbh778899 Jul 12, 2024
2af9a45
update style so it won't looks strange
cbh778899 Jul 12, 2024
d3a088d
update request function to directly return json on demand
cbh778899 Jul 12, 2024
b08a924
update isRegister to false after login
cbh778899 Jul 12, 2024
c956ea6
rename type to role
cbh778899 Jul 12, 2024
3188608
can normally save/load history now
cbh778899 Jul 12, 2024
d12f8d3
fix url
cbh778899 Jul 12, 2024
bf6dc17
fix history & conversation page switch when login/logout
cbh778899 Jul 12, 2024
f165986
add header Content-Type: application/json to patch request as well
cbh778899 Jul 13, 2024
89d2d50
implement updateuserinfo
cbh778899 Jul 13, 2024
8387b01
update login page style
cbh778899 Jul 13, 2024
23f8e38
implement show message
cbh778899 Jul 13, 2024
1432c9d
handle request HTTP error
cbh778899 Jul 13, 2024
6ab47c7
return to check if need to show message
cbh778899 Jul 13, 2024
c85f594
update style
cbh778899 Jul 13, 2024
d8977fb
implement change user info
cbh778899 Jul 13, 2024
8e1bd88
fix bugs user cannot click
cbh778899 Jul 13, 2024
40574ee
updateall after update info
cbh778899 Jul 13, 2024
4fc0f14
fix bugs when user switched page and cannot toggle sidebar expand
cbh778899 Jul 13, 2024
c7571c9
update style
cbh778899 Jul 13, 2024
b632a9f
implement save user info to local
cbh778899 Jul 13, 2024
6d29b7f
update message background
cbh778899 Jul 13, 2024
23d0860
add function to create a fake dialog, so we make sure messages showes…
cbh778899 Jul 14, 2024
e3581a6
implement createDialog to replace dialogs
cbh778899 Jul 14, 2024
4ac162e
remove dialog global styles
cbh778899 Jul 14, 2024
fb780c8
update dialog style
cbh778899 Jul 14, 2024
f2ebefe
update version
cbh778899 Jul 14, 2024
67333e3
fix event propagation bug
cbh778899 Jul 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
255 changes: 253 additions & 2 deletions components/account-page/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,257 @@
import useUser from '../../global/useUser.js'
import capitalizeFirstLetter from "../../tools/capitalizeFirstLetter.js";
import getSVG from "../../tools/svgs.js";
import showMessage from "../../tools/message.js";
import createDialog from '../../tools/dialog.js';

let input_details_main = null, init = false, toggleDialog;
let current_user = {}, isRegister = false, user_info_saved = localStorage.getItem('saved-user-login-info');
if(user_info_saved) user_info_saved = JSON.parse(user_info_saved);

const {
register, login, logout, updateUserInfo
} = useUser(user=>{
if(!input_details_main) return;
current_user = user;
createInputDetailsPage();
});

function saveUserInfo(save_info = null) {
if(!save_info) {
user_info_saved && localStorage.removeItem('saved-user-login-info');
user_info_saved = null;
} else {
user_info_saved = save_info
localStorage.setItem('saved-user-login-info', JSON.stringify(save_info))
}
}

const account_fields = {
login: [
{ index: 'username' },
{ index: 'password', type: 'password' }
],
register: [
{ index: 'username' },
{ index: 'email' },
{ index: 'password', type: 'password' },
{ index: 'repeat-password', title: 'Confirm Password', type: 'password' }
],
logged_in: [
{ index: 'username', readonly: true },
{ index: 'email', title: 'Update Email' },
{ index: 'new-password', title: 'New Password', type: 'password' },
{ index: 'repeat-new-password', title: 'Confirm New Password', type: 'password' },
]
}

function createInputDetailsPage() {
input_details_main.innerHTML = '';
// input fields
account_fields[
current_user.logged_in ? 'logged_in' :
isRegister ? 'register' : 'login'
]
.forEach(e=>{
const field = {...e};
if(current_user.logged_in) {
if(e.index === 'username') field.value = current_user.username;
else if(e.index === 'email') field.value = current_user.email;
} else if(!isRegister && user_info_saved) {
if(e.index === 'username') field.value = user_info_saved.username;
else if(e.index === 'password') field.value = user_info_saved.password;
}
input_details_main.appendChild(
createAccountInputFields(field)
)
})

if(!current_user.logged_in) {
const keep_login = document.createElement('div')
keep_login.className = 'keep-login clickable';

keep_login.innerHTML = `
<input type='checkbox' name='keep-login'${user_info_saved?' checked':''}>
<div class="title">Keep me logged in!</div>`

input_details_main.appendChild(keep_login);
keep_login.onclick = () => {
keep_login.firstElementChild.click();
}
keep_login.firstElementChild.onclick = evt => evt.stopPropagation();
}

// hr
input_details_main.insertAdjacentHTML("beforeend", "<hr>")
// buttons
const submit_btn = document.createElement('button');
submit_btn.type = 'submit';
submit_btn.className = 'function-btn clickable';
input_details_main.appendChild(submit_btn);

const functional_btn = document.createElement('button');
functional_btn.type = 'button';
functional_btn.className = 'function-btn reverse-color clickable';
input_details_main.appendChild(functional_btn);

function updateBtnText() {
submit_btn.textContent =
current_user.logged_in ? 'Update' :
isRegister ? 'Register Now' : 'Login Now';

functional_btn.textContent =
current_user.logged_in ? 'Logout' :
isRegister ? 'I want to Login!' : 'I want to Register!';
}
updateBtnText();

functional_btn.onclick = evt => {
evt.preventDefault();
if(current_user.logged_in) {
logout().then(()=>showMessage('User logged out.'));
} else {
updateBtnText();
isRegister = !isRegister;
createInputDetailsPage();
}
}

}

function submitDetails(evt) {
evt.preventDefault();

if(current_user.logged_in) {
const email = evt.target.email.value;
const new_password = evt.target['new-password'].value;
const repeat_new_password = evt.target['repeat-new-password'].value;
const submit_values = {}
if(email && email !== current_user.email) {
submit_values.email = email;
}
if(new_password) {
if(repeat_new_password !== new_password) {
showMessage("Passwords are not same!", { type: 'err' })
return;
}
submit_values.password = new_password;
}
updateUserInfo(submit_values).then(res=>{
if(res) {
submit_values.email && showMessage('Email updated.');
submit_values.password && showMessage('Password updated.');
evt.target['new-password'].value = ''
evt.target['repeat-new-password'].value = ''
} else {
showMessage('Update information failed!', { type: 'err' })
}
});
} else {
const username = evt.target.username.value;
const password = evt.target.password.value;
const keep_login = evt.target['keep-login'].checked;
if(isRegister) {
const email = evt.target.email.value;
const repeat_password = evt.target['repeat-password'].value;

if(password !== repeat_password) {
showMessage("Passwords are not same!", { type: 'err' })
return;
}
register(username, email, password).then(res=>{
if(res) {
isRegister=false;
showMessage('Register Success!', { type: 'success' });
saveUserInfo(keep_login && { username, password });
} else showMessage('Register failed!', { type: 'err' });
});
} else {
login(username, password).then(res=>{
if(res) {
showMessage(`Welcome back, ${username}`);
saveUserInfo(keep_login && { username, password });
}
else showMessage('Login failed!', { type: 'err' })
});
}
}
}

export default function createAccountPage() {
document.getElementById('user-avatar')
.insertAdjacentHTML("afterbegin", getSVG('person-fill'))
if(init && toggleDialog) {
toggleDialog();
return;
}

const [account_container, controller] = createDialog();
toggleDialog = controller.toggleModal;

const account_main_page = document.createElement('form');
account_main_page.className = 'account-main';
account_main_page.onclick = evt => evt.stopPropagation();
account_main_page.onsubmit = submitDetails;

account_main_page.insertAdjacentHTML("afterbegin", `
<div class='logo-image'><img src='/medias/SkywardAI.png'></div>`)

input_details_main = document.createElement('div');
input_details_main.className = 'input-details-main';

account_main_page.appendChild(input_details_main);
account_container.appendChild(account_main_page)
document.body.appendChild(account_container)

createInputDetailsPage();

init = true;
controller.showModal();
}

function createAccountInputFields({
index, title = '', type = '',
readonly = false, value = ''
}) {
const field_container = document.createElement('div');
field_container.className = 'account-field-container';

const title_element = document.createElement('div');
title_element.textContent = title || capitalizeFirstLetter(index);
title_element.className = 'title'

const input = document.createElement('input');
input.type = type || 'text';
input.name = index;
input.value = value;
input.placeholder = ' '
input.autocomplete = 'off'
if(readonly) input.readOnly = 'true';

field_container.onclick = () => input.focus();
field_container.appendChild(title_element);
field_container.appendChild(input);

if(type === 'password') {
let show_password = false;

const eye_icon = document.createElement('div');
eye_icon.className = 'password-eye-icon clickable';

const toggleShowPassword = () => {
if(!show_password) {
input.type = 'password';
eye_icon.innerHTML = getSVG('eye');
eye_icon.title = 'Show Password';
} else {
input.type = 'text';
eye_icon.innerHTML = getSVG('eye-slash');
eye_icon.title = 'Hide Password';
}
show_password = !show_password;
}
toggleShowPassword();
eye_icon.onclick = toggleShowPassword;
field_container.appendChild(eye_icon);
}

return field_container;
}
53 changes: 37 additions & 16 deletions components/chat-page/chatMain.js
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import useConversation from "../../global/useConversation.js";
import useHistory from "../../global/useHistory.js";
import useModelSettings from "../../global/useModelSettings.js";
import { formatJSON } from "../../tools/conversationFormat.js";
import showMessage from "../../tools/message.js";
import request from "../../tools/request.js";
import getSVG from "../../tools/svgs.js";

@@ -20,7 +21,16 @@ const {
submit_icon && submit_icon.classList.toggle('pending', conversation.pending)
if(c.id === conversation.id) return;
conversation = c;
if(!conversation.id) return;
if(!conversation.id) {
if(main_elem) {
main_elem.innerHTML = `
<div class='greeting'>
Please select a ticket or start a new conversation on left.
</div>`
document.getElementById('submit-chat').innerHTML = '';
}
return;
}

updateConversation();
buildForm();
@@ -66,8 +76,7 @@ export default function createChatMain(main, toggleExpand, openModelSetting) {
</div>
</div>`)

if(!toggle_expand) toggle_expand = toggleExpand;

toggle_expand = toggleExpand;
document.getElementById('submit-chat').onsubmit=submitContent;
main_elem = document.getElementById('conversation-main');
document.getElementById('toggle-sidebar-expand').onclick = toggle_expand;
@@ -77,8 +86,8 @@ export default function createChatMain(main, toggleExpand, openModelSetting) {
formatJSON(conversation, getHistory(conversation.id))
}
}

modelSettingsRemount();

if(conversationReMount() && conversation.id) {
updateConversation();
buildForm();
@@ -114,7 +123,13 @@ function buildForm() {

function submitContent(evt) {
evt.preventDefault();
if(conversation.pending) return;
if(conversation.pending) {
showMessage(
"Please wait until assistant finished response.",
{ type: 'warn' }
)
return;
}

const content = evt.target['send-content'].value;
content && (
@@ -129,14 +144,16 @@ async function sendMessage(message, send) {
togglePending();
if(!conversation.history.length) {
main_elem.innerHTML = ''
updateHistoryName(conversation.id, message.substring(0, 20))
const message_len = message.length;
updateHistoryName(conversation.id,
`${message.substring(0, 25)}${message_len > 25 ? '...' : ''}`)
}
main_elem.appendChild(createBlock('out', message)[0]);
main_elem.appendChild(createBlock('user', message)[0]);
main_elem.scrollTo({
top: main_elem.scrollHeight,
behavior: 'smooth'
})
const [bot_answer, updateMessage] = createBlock('in');
const [bot_answer, updateMessage] = createBlock('assistant');
main_elem.appendChild(bot_answer);

const response = await request('chat', {
@@ -145,14 +162,14 @@ async function sendMessage(message, send) {
sessionUuid: conversation.id || "uuid",
message, ...model_settings
}
})
}, true)

const content = await send(response, updateMessage);
togglePending();

appendConversationMessage([
{ type: 'out', message },
{ type: 'in', message: content}
{ role: 'user', message },
{ role: 'assistant', message: content}
], conversation.id)
}

@@ -198,21 +215,25 @@ function updateConversation() {
}

main_elem.innerHTML = ''
conversation.history.forEach(({type, message})=>{
main_elem.appendChild(createBlock(type, message)[0])
conversation.history.forEach(({role, message})=>{
main_elem.appendChild(createBlock(role, message)[0])
})
main_elem.scrollTo({
top: main_elem.scrollHeight,
behavior: 'smooth'
})
}

function createBlock(type, msg = '') {
function createBlock(role, msg = '') {
const block = document.createElement('div');
block.className = `conversation-block sender-${type}`;
block.className = `conversation-block sender-${role}`;

const message = document.createElement('div');
message.className = 'message';

block.appendChild(message);

if(type === 'in') {
if(role === 'assistant') {
message.innerHTML = `
${getSVG('circle-fill', 'dot-animation dot-1')}
${getSVG('circle-fill', 'dot-animation dot-2')}
Loading