From 25b22786c171567827b1751ecd8cc26048ada7d5 Mon Sep 17 00:00:00 2001 From: ellevanova Date: Mon, 2 Oct 2023 20:49:15 +0400 Subject: [PATCH 01/43] added utils --- src/utils/Http.ts | 14 ++--- src/utils/object.utils.ts | 106 ++++++++++++++++++++++++++++++++++++-- src/utils/string.utils.ts | 65 +++++++++++++++++++++++ 3 files changed, 171 insertions(+), 14 deletions(-) create mode 100644 src/utils/string.utils.ts diff --git a/src/utils/Http.ts b/src/utils/Http.ts index bbfddeaa3..59a9fa20e 100644 --- a/src/utils/Http.ts +++ b/src/utils/Http.ts @@ -1,3 +1,5 @@ +import {queryStringify} from "./string.utils.ts"; + enum METHODS { GET = 'GET', POST = 'POST', @@ -15,17 +17,7 @@ type IOptionsRequest = { type HTTPMethod = (url: string, options?: IOptionsRequest) => Promise -/** - * Get string of query params from object params - * @param data - */ -function queryStringify(data: object) { - let result = '?'; - result = result + Object.entries(data).map(([key, value]) => { - return `${key}=${Array.isArray(value) ? value.join(',') : String(value)}` - }).join("&") - return result; -} + class HTTPTransport { get: HTTPMethod = (url, options = {}) => { diff --git a/src/utils/object.utils.ts b/src/utils/object.utils.ts index 85aee1765..0c5521eb7 100644 --- a/src/utils/object.utils.ts +++ b/src/utils/object.utils.ts @@ -1,4 +1,7 @@ -export const isDeepEqual=(object1:{[index: string]:T}, object2:{[index: string]:T}) => { +export type Indexed = { + [key in string]: T; +}; +export const isDeepEqual = (object1: { [index: string]: T }, object2: { [index: string]: T }) => { const objKeys1 = Object.keys(object1); const objKeys2 = Object.keys(object2); @@ -11,7 +14,7 @@ export const isDeepEqual=(object1:{[index: string]:T}, object2 const isObjects = isObject(value1) && isObject(value2); - if ((isObjects && !isDeepEqual(value1 as { [index: string]: T } , value2 as { [index: string]: T })) || + if ((isObjects && !isDeepEqual(value1 as { [index: string]: T }, value2 as { [index: string]: T })) || (!isObjects && value1 !== value2) ) { return false; @@ -20,6 +23,103 @@ export const isDeepEqual=(object1:{[index: string]:T}, object2 return true; }; -const isObject = (object:object) => { +export function cloneDeep(obj: any): any { + if (!isObject(obj)) { + return obj; + } + + const clonedObj = Array.isArray(obj) ? [] : {}; + + for (let key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + // @ts-ignore + clonedObj[key] = cloneDeep(obj[key]); + } + } + + return clonedObj; +} + +export const isObject = (object: object | unknown) => { return object != null && typeof object === "object"; }; + +/** + * Напишите функцию, которая объединит два объекта с сохранением их уникальных ключей. Порядок ключей важен. + * merge({a: {b: {a: 2}}, d: 5}, {a: {b: {c: 1}}}); + * /* + * { + * a: { + * b: { + * a: 2, + * c: 1, + * } + * }, + * d: 5, + * } + * + */ + +export const merge = (lhs: Indexed, rhs: Indexed): Indexed => { + let result = lhs; + const objKeys1 = Object.keys(lhs); + const objKeys2 = Object.keys(rhs); + for (const key of objKeys1) { + if (objKeys2.indexOf(key) === -1) result[key] = lhs[key]; + else { + if (typeof lhs[key] === 'object' && typeof rhs[key] === 'object') { + // @ts-ignore + result[key] = merge(lhs[key], rhs[key]); + } else { + result[key] = lhs[key]; + } + } + } + + for (const key of objKeys2) { + if (objKeys1.indexOf(key) === -1) result[key] = rhs[key]; + } + return result; + /* + for (let p in rhs) { + if (!rhs.hasOwnProperty(p)) { + continue; + } + + try { + if (rhs[p].constructor === Object) { + rhs[p] = merge(lhs[p] as Indexed, rhs[p] as Indexed); + } else { + lhs[p] = rhs[p]; + } + } catch(e) { + lhs[p] = rhs[p]; + } + } + + return lhs; + */ + +} + +/** + * set, которая получает путь к вложенному свойству объекта и устанавливает значение в это свойство. + * Если поля не существует, его нужно создать по указанному пути. + * Проверьте, что path — строка, иначе нужно выбросить ошибку 'path must be string' + * @param object + * @param path + * @param value + */ +export function set(object: Indexed | unknown, path: string, value: unknown): Indexed | unknown { + + if (!isObject(object)) return object; + if (typeof path !== 'string') throw Error('path must be string'); + let _object = createObjectFromString(path, value) + return merge(object as Indexed, _object) +} + +function createObjectFromString(string: string, value: any) { + return string.split('.').reduceRight((acc, key) => ({ + [key]: acc, + }), value as any); +} diff --git a/src/utils/string.utils.ts b/src/utils/string.utils.ts new file mode 100644 index 000000000..3538bdae0 --- /dev/null +++ b/src/utils/string.utils.ts @@ -0,0 +1,65 @@ +import {isObject} from "./object.utils.ts"; + + +export type StringIndexed = Record; + +/** + * Напишите функцию, которая умеет удалять из начала и конца строки пробельные или отдельно заданные пользовательские символы. Удаление пробелов из начала и конца строк — поведение по умолчанию. Пробел необязательно должен быть передан в качестве аргумента. + * @param str -строка для анализа + * @param deleted -удаляемые символы + */ +export const trim = (str: string, deleted?: string): string => { + if (str && !deleted) return str.trim(); + const chars = deleted?.split("").map(item => "\\" + item).join(''); + const regexp = new RegExp(`[${chars}]`, 'g'); + const array = str.split(''); + let startWord = 0; + for (let i = 0; i < array.length; i++) { + if (!array[i].match(regexp)) { + startWord = i; + break; + } + } + let endWord = 0; + for (let i = array.length - 1; i > startWord; i--) { + if (!array[i].match(regexp)) { + endWord = i + 1; + break; + } + } + + return str.substring(startWord, endWord); +} + +/** + * Get string of query params from object params + * @param data + */ +export function queryStringify(data: StringIndexed) { + if(!isObject(data)){ + throw Error('Input must be an object') + } + let result:string[] = []; + Object.entries(data).map(([key, value]) => { + valueToString(key,value,result) + }) + return '?'+result.join("&"); +} + +export const objToString=(keyItog:string,value:object,resultArray:string[])=>{ + Object.entries(value).map(([key, value]) => { + valueToString(`${keyItog}[${key}]`,value,resultArray) + }) +} + +export const arrayToString=(key:string,value:Array,resultArray:string[])=>{ + value.map((item,index)=>{ + valueToString(`${key}[${String(index)}]`,item,resultArray) + }) +} + +export const valueToString=(key:string,value:unknown,result:string[])=>{ + if(Array.isArray(value))return arrayToString(key,value,result); + if(isObject(value))return objToString(key,value as Object,result); + result.push (`${key}=${String(value)}`); +} From c587c0a1a65e4c63e583f4863d1d5621e699c1ed Mon Sep 17 00:00:00 2001 From: ellevanova Date: Wed, 4 Oct 2023 18:48:56 +0400 Subject: [PATCH 02/43] added router --- src/utils/Block.ts | 7 ++++ src/utils/Route.ts | 55 +++++++++++++++++++++++++++ src/utils/Router.ts | 90 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 src/utils/Route.ts create mode 100644 src/utils/Router.ts diff --git a/src/utils/Block.ts b/src/utils/Block.ts index 29fef2cff..379ef3100 100644 --- a/src/utils/Block.ts +++ b/src/utils/Block.ts @@ -203,6 +203,13 @@ export class Block { }); } + public hide(){ + console.log('hide') + } + public show(){ + console.log('show') + } + } diff --git a/src/utils/Route.ts b/src/utils/Route.ts new file mode 100644 index 000000000..6901878b7 --- /dev/null +++ b/src/utils/Route.ts @@ -0,0 +1,55 @@ +import Block, {IProps} from "./Block.ts"; + +class Route { + private _pathname: string; + private _blockClass: Block; + private _block:Block|null=null; + private _props: IProps; + + constructor(pathname:string, view:Block, props:IProps) { + this._pathname = pathname; + this._blockClass = view; + this._block = null; + this._props = props; + } + + /** + * navigate — метод для отображения вьюшки, если переданный URL совпадает с URL текущего Route; + * @param pathname + */ + navigate(pathname:string) { + if (this.match(pathname)) { + this._pathname = pathname; + this.render(); + } + } + + /** + * leave — вызывает hide у элемента; + */ + leave() { + if (this._block) { + this._block.hide(); + } + } + + match(pathname:string) { + return pathname===this._pathname; + } + +/** + * render — создаёт блок, если тот ещё не был создан (нужно создавать блок только после первого + * перехода на страницу), иначе вызывает у блока метод show. + * */ + render() { + if (!this._block) { + console.log(this._props,this._blockClass); + //this._block = new this._blockClass(this._props); + //render(this._props.rootQuery, this._block); + return; + } + + this._block.show(); + } +} +export default Route diff --git a/src/utils/Router.ts b/src/utils/Router.ts new file mode 100644 index 000000000..09e03b934 --- /dev/null +++ b/src/utils/Router.ts @@ -0,0 +1,90 @@ +import Block, {IProps} from "./Block.ts"; +import Route from "./Route.ts"; + +class Router { + private static __instance: any; + private routes: any[] | undefined; + private history: History | undefined; + private _currentRoute: null | undefined; + private _rootQuery: IProps | undefined; + constructor(rootQuery:IProps) { + if (Router.__instance) { + return Router.__instance; + } + + this.routes = []; + this.history = window.history; + this._currentRoute = null; + this._rootQuery = rootQuery; + + Router.__instance = this; + } + + /** + * use — регистрирует блок по пути в роут и возвращает себя — чтобы можно было выстроить в цепочку; + * @param pathname + * @param block + */ + use(pathname:string, block:Block) { + const route = new Route(pathname, block, {rootQuery: this._rootQuery}); + this.routes?.push(route); + return this; + } + + /** + * start — по событию onpopstate запускает приложение. + */ + start() { + // Реагируем на изменения в адресной строке и вызываем перерисовку + window.onpopstate = event => { + // @ts-ignore + this._onRoute(event?.currentTarget?.location?.pathname); + }; + + this._onRoute(window.location.pathname); + } + + _onRoute(pathname:string) { + const route = this.getRoute(pathname); + if (!route) { + return; + } + + if (this._currentRoute && this._currentRoute !== route) { + // @ts-ignore + this._currentRoute.leave(); + } + + this._currentRoute = route; + route.render(route, pathname); + } + + /** + * go — переходит на нужный роут и отображает нужный блок; + * @param pathname + */ + go(pathname:string) { + this.history?.pushState({}, "", pathname); + this._onRoute(pathname); + } + + /** + * back — возвращает в прошлое состояние и показывает блок, соответствующий тому состоянию; + */ + back() { + this.history?.back(); + + } + + /** + * forward — переходит в следующие состояние и показывает соответствующий блок; + */ + forward() { + this.history?.forward(); + + } + + getRoute(pathname:string) { + return this.routes?.find(route => route.match(pathname)); + } +} From eeffb5823c43e9db87037c7e9eb76776b11d643e Mon Sep 17 00:00:00 2001 From: ellevanova Date: Wed, 4 Oct 2023 20:15:49 +0400 Subject: [PATCH 03/43] added routing --- src/components/chat-list/chat-list.ts | 2 +- src/components/form-profile/form-profile.ts | 6 +-- src/index.html | 1 - src/main.ts | 55 +++++++-------------- src/pages/login/login.ts | 3 ++ src/utils/Block.ts | 6 ++- src/utils/Route.ts | 12 ++--- src/utils/Router.ts | 19 ++++--- 8 files changed, 48 insertions(+), 56 deletions(-) diff --git a/src/components/chat-list/chat-list.ts b/src/components/chat-list/chat-list.ts index dce9d767f..697d91d61 100644 --- a/src/components/chat-list/chat-list.ts +++ b/src/components/chat-list/chat-list.ts @@ -27,7 +27,7 @@ export class ChatList extends Block { return (`
`: `
- {{{Link caption="Change IUser Data" page="pageProfileEdit" type='success' linkLine=true }}} - {{{Link caption="Change Password" page="pagePasswordEdit" type='success' linkLine=true }}} - {{{Link caption="Cancel" page=button-page type='danger' }}} + {{{Link caption="Change IUser Data" href='/profile-edit' type='success' linkLine=true }}} + {{{Link caption="Change Password" href='/password-edit' type='success' linkLine=true }}} + {{{Link caption="Cancel" href='/chat' type='danger' }}}
`}
diff --git a/src/index.html b/src/index.html index ed81248b3..bc931504c 100644 --- a/src/index.html +++ b/src/index.html @@ -11,4 +11,3 @@
- diff --git a/src/main.ts b/src/main.ts index b341476d2..a8c547fa1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,9 @@ import './styles/main.pcss'; import * as Components from './components'; import * as Pages from './pages'; import {registerComponent} from "./utils/registerComponents"; -import {Block} from "./utils/Block.ts"; +import Router from "./utils/Router.ts"; +import Block from "./utils/Block.ts"; + const allComponents = { @@ -24,43 +26,22 @@ const allComponents = { 'FormAuth': Components.FormAuth, 'FormProfile': Components.FormProfile, } -const pages:{[index: string]:{component:unknown}} = { - "allComponents": { - component: Pages.AllComponentsPage - }, - "allPages": {component: Pages.AllPages}, - "loginPage": {component: Pages.LoginPage}, - "pageRegistration": {component: Pages.PageRegistration}, - "pageProfile": {component: Pages.PageProfile}, - "pageProfileEdit": {component: Pages.PageProfileEdit}, - "pagePasswordEdit": {component: Pages.PagePasswordEdit}, - "page500": {component: Pages.Page500}, - "page404": {component: Pages.Page500}, - "pageChat": {component: Pages.PageChat}, -}; - Object.entries(allComponents).forEach(([name, component]) => { registerComponent(name, component); }); -const navigate = (page: string) => { - const app = document.getElementById('app'); - const Component = pages[page].component as unknown as typeof Block; - //const props = pages[page].props || {}; - const component = new Component({events:{}}); - const htmlElement = component.getContent(); - if (!app?.firstElementChild) app?.append(document.createElement('div')); - if(htmlElement) - app?.firstElementChild?.replaceWith(htmlElement); -} -document.addEventListener('DOMContentLoaded', () => navigate('allPages')); -document.addEventListener('click', (e: Event) => { - if (!e) return; - if(!e.target)return; - const page =( e.target).getAttribute('page'); - if (page) { - navigate(page); - e.preventDefault(); - e.stopImmediatePropagation(); - } -}); + +const router = new Router(".app"); + +// Можно обновиться на /user и получить сразу пользователя +router.use("/", Pages.PageChat as unknown as Block) + .use("/all", Pages.AllComponentsPage as unknown as Block) + .use("/login", Pages.LoginPage as unknown as Block) + .use("/registration", Pages.PageRegistration as unknown as Block) + .use("/profile", Pages.PageProfile as unknown as Block) + .use("/profile-edit", Pages.PageProfileEdit as unknown as Block) + .use("/password-edit", Pages.PagePasswordEdit as unknown as Block) + .use("/404", Pages.Page404 as unknown as Block) + .use("/500", Pages.Page500 as unknown as Block) + .use("/chat", Pages.PageChat as unknown as Block) + .start(); diff --git a/src/pages/login/login.ts b/src/pages/login/login.ts index 3c5941caa..0818376bc 100644 --- a/src/pages/login/login.ts +++ b/src/pages/login/login.ts @@ -1,4 +1,6 @@ import {IProps,Block} from "../../utils/Block"; +import Router from "../../utils/Router.ts"; + export interface ILoginPageProps extends IProps { onLogin:(event:Event)=>void @@ -16,6 +18,7 @@ export class LoginPage extends Block { login, password }) + if(login&&password)Router.getRouter().go('/chat'); } } diff --git a/src/utils/Block.ts b/src/utils/Block.ts index 379ef3100..eb37a38f9 100644 --- a/src/utils/Block.ts +++ b/src/utils/Block.ts @@ -207,7 +207,11 @@ export class Block { console.log('hide') } public show(){ - console.log('show') + const app = document.getElementById('app'); + const htmlElement = this.getContent(); + if (!app?.firstElementChild) app?.append(document.createElement('div')); + if(htmlElement) + app?.firstElementChild?.replaceWith(htmlElement); } diff --git a/src/utils/Route.ts b/src/utils/Route.ts index 6901878b7..a3b6f0768 100644 --- a/src/utils/Route.ts +++ b/src/utils/Route.ts @@ -2,11 +2,11 @@ import Block, {IProps} from "./Block.ts"; class Route { private _pathname: string; - private _blockClass: Block; + private readonly _blockClass: Block; private _block:Block|null=null; - private _props: IProps; + private readonly _props: IProps; - constructor(pathname:string, view:Block, props:IProps) { + constructor(pathname:string, view:Block, props:object) { this._pathname = pathname; this._blockClass = view; this._block = null; @@ -43,9 +43,9 @@ class Route { * */ render() { if (!this._block) { - console.log(this._props,this._blockClass); - //this._block = new this._blockClass(this._props); - //render(this._props.rootQuery, this._block); + // @ts-ignore + this._block = new this._blockClass(this._props); + this.render(); return; } diff --git a/src/utils/Router.ts b/src/utils/Router.ts index 09e03b934..47b2d3bff 100644 --- a/src/utils/Router.ts +++ b/src/utils/Router.ts @@ -1,13 +1,13 @@ -import Block, {IProps} from "./Block.ts"; +import Block from "./Block.ts"; import Route from "./Route.ts"; class Router { - private static __instance: any; - private routes: any[] | undefined; + private static __instance: Router; + private routes: Route[] | undefined; private history: History | undefined; - private _currentRoute: null | undefined; - private _rootQuery: IProps | undefined; - constructor(rootQuery:IProps) { + private _currentRoute: null | Route=null; + private _rootQuery: string | undefined; + constructor(rootQuery:string) { if (Router.__instance) { return Router.__instance; } @@ -20,6 +20,9 @@ class Router { Router.__instance = this; } + public static getRouter(){ + return this.__instance; + } /** * use — регистрирует блок по пути в роут и возвращает себя — чтобы можно было выстроить в цепочку; * @param pathname @@ -56,7 +59,7 @@ class Router { } this._currentRoute = route; - route.render(route, pathname); + route.render(); } /** @@ -88,3 +91,5 @@ class Router { return this.routes?.find(route => route.match(pathname)); } } + +export default Router; From ed5486ce2bef3bd3277c697d6251d1e47f86e42b Mon Sep 17 00:00:00 2001 From: ellevanova Date: Wed, 4 Oct 2023 20:23:25 +0400 Subject: [PATCH 04/43] added routing --- src/components/chat-list/chat-list.ts | 2 +- src/components/form-profile/form-profile.ts | 4 ++-- src/main.ts | 10 +++++----- src/pages/chat-page/page-chat.ts | 1 - src/pages/login/login.ts | 2 +- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/components/chat-list/chat-list.ts b/src/components/chat-list/chat-list.ts index 697d91d61..23a34af67 100644 --- a/src/components/chat-list/chat-list.ts +++ b/src/components/chat-list/chat-list.ts @@ -27,7 +27,7 @@ export class ChatList extends Block { return (`
`: `
- {{{Link caption="Change IUser Data" href='/profile-edit' type='success' linkLine=true }}} + {{{Link caption="Change IUser Data" href='/settings-edit' type='success' linkLine=true }}} {{{Link caption="Change Password" href='/password-edit' type='success' linkLine=true }}} - {{{Link caption="Cancel" href='/chat' type='danger' }}} + {{{Link caption="Cancel" href='/messenger' type='danger' }}}
`}
diff --git a/src/main.ts b/src/main.ts index a8c547fa1..8ed8e8c3d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -34,14 +34,14 @@ Object.entries(allComponents).forEach(([name, component]) => { const router = new Router(".app"); // Можно обновиться на /user и получить сразу пользователя -router.use("/", Pages.PageChat as unknown as Block) +router.use("/", Pages.LoginPage as unknown as Block) .use("/all", Pages.AllComponentsPage as unknown as Block) .use("/login", Pages.LoginPage as unknown as Block) - .use("/registration", Pages.PageRegistration as unknown as Block) - .use("/profile", Pages.PageProfile as unknown as Block) - .use("/profile-edit", Pages.PageProfileEdit as unknown as Block) + .use("/sign-up", Pages.PageRegistration as unknown as Block) + .use("/settings", Pages.PageProfile as unknown as Block) + .use("/settings-edit", Pages.PageProfileEdit as unknown as Block) .use("/password-edit", Pages.PagePasswordEdit as unknown as Block) .use("/404", Pages.Page404 as unknown as Block) .use("/500", Pages.Page500 as unknown as Block) - .use("/chat", Pages.PageChat as unknown as Block) + .use("/messenger", Pages.PageChat as unknown as Block) .start(); diff --git a/src/pages/chat-page/page-chat.ts b/src/pages/chat-page/page-chat.ts index 2fa57cdb2..880cb1ed3 100644 --- a/src/pages/chat-page/page-chat.ts +++ b/src/pages/chat-page/page-chat.ts @@ -19,7 +19,6 @@ export class PageChat extends Block { messageList:mockListMessages, events:{} } - super(props); } diff --git a/src/pages/login/login.ts b/src/pages/login/login.ts index 0818376bc..545de57d2 100644 --- a/src/pages/login/login.ts +++ b/src/pages/login/login.ts @@ -18,7 +18,7 @@ export class LoginPage extends Block { login, password }) - if(login&&password)Router.getRouter().go('/chat'); + if(login&&password)Router.getRouter().go('/messenger'); } } From 7186ddcc717b78c40907450e807750981c9ce603 Mon Sep 17 00:00:00 2001 From: ellevanova Date: Wed, 4 Oct 2023 21:52:21 +0400 Subject: [PATCH 05/43] added api auth --- src/api/auth.ts | 31 ++++++++++++++++++++++++++ src/config.ts | 2 +- src/models/IUser.ts | 5 ++++- src/pages/registration/registration.ts | 15 +++++++++++++ src/services/auth.ts | 13 +++++++++++ src/utils/Http.ts | 20 ++++++++++------- 6 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 src/api/auth.ts create mode 100644 src/services/auth.ts diff --git a/src/api/auth.ts b/src/api/auth.ts new file mode 100644 index 000000000..39419e192 --- /dev/null +++ b/src/api/auth.ts @@ -0,0 +1,31 @@ +import HTTPTransport from "../utils/Http.ts"; +import {IAuthData, IUser} from "../models/IUser.ts"; + +export class AuthApi { + private httpTransport = new HTTPTransport(); + private readonly baseUrl: string | null = null; + + constructor(baseUrl?: string) { + if (baseUrl) this.baseUrl = baseUrl; + } + + public async signUp(userData: IUser) { + const result = await this.httpTransport.post(this.baseUrl + '/signup', {data: userData}); + console.log(result); + } + + public async signIn(userData: IAuthData) { + const result = await this.httpTransport.post(this.baseUrl + '/signin', {data: userData}); + console.log(result); + } + public async getAuthUser() { + const result = await this.httpTransport.get(this.baseUrl + '/user'); + console.log(result); + } + public async logOut() { + const result = await this.httpTransport.post(this.baseUrl + '/logout'); + console.log(result); + } +} + +export default AuthApi diff --git a/src/config.ts b/src/config.ts index 47a370984..6d6590997 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,2 +1,2 @@ export const urlImages = '/'; - +export const BASE_API_URL='https://ya-praktikum.tech/api/v2'; diff --git a/src/models/IUser.ts b/src/models/IUser.ts index 5f9f546a0..1fe9034f3 100644 --- a/src/models/IUser.ts +++ b/src/models/IUser.ts @@ -9,4 +9,7 @@ export interface IUser { avatar?: string; } - +export interface IAuthData { + login: string; + password: string; +} diff --git a/src/pages/registration/registration.ts b/src/pages/registration/registration.ts index 067fdf58a..5ab04af75 100644 --- a/src/pages/registration/registration.ts +++ b/src/pages/registration/registration.ts @@ -1,4 +1,6 @@ import {IProps,Block} from "../../utils/Block.ts"; +import {signup} from "../../services/auth.ts"; +import {IUser} from "../../models/IUser.ts"; export interface IPageRegistrationProps extends IProps { onLogin:(event:Event)=>void, @@ -26,6 +28,19 @@ export class PageRegistration extends Block { phone, email }) + + if(password===password2){ + const data={ + first_name, + second_name, + login, + email, + password, + phone + } as IUser; + signup(data) + } + } } diff --git a/src/services/auth.ts b/src/services/auth.ts new file mode 100644 index 000000000..bee6d3d46 --- /dev/null +++ b/src/services/auth.ts @@ -0,0 +1,13 @@ +import {IUser} from "../models/IUser.ts"; +import AuthApi from "../api/auth.ts"; + +const authApi=new AuthApi('/auth'); +const signup = async (data: IUser) => { + await authApi.signUp(data); + + +} + +export { + signup, +} diff --git a/src/utils/Http.ts b/src/utils/Http.ts index 59a9fa20e..6c0406d15 100644 --- a/src/utils/Http.ts +++ b/src/utils/Http.ts @@ -1,4 +1,5 @@ import {queryStringify} from "./string.utils.ts"; +import {BASE_API_URL} from "../config.ts"; enum METHODS { GET = 'GET', @@ -8,7 +9,7 @@ enum METHODS { } type IOptionsRequest = { - data?: string; + data?: object; method?: METHODS.GET | METHODS.POST | METHODS.PUT | METHODS.DELETE; timeout?: number; headers?: Record; @@ -20,25 +21,28 @@ type HTTPMethod = (url: string, options?: IOptionsRequest) => Promise class HTTPTransport { + private readonly baseUrl:string='' + constructor(base_url?:string) { + this.baseUrl=base_url||BASE_API_URL; + } get: HTTPMethod = (url, options = {}) => { - return this.request(url, { + return this.request(this.baseUrl+url+queryStringify(options.params || {}) || '', { ...options, - data: queryStringify(options.params || {}) || '', method: METHODS.GET }, options.timeout); }; put: HTTPMethod = (url, options = {}) => { - return this.request(url, {...options, method: METHODS.PUT}, options.timeout); + return this.request(this.baseUrl+url, {...options, method: METHODS.PUT,headers:{'Content-Type': 'application/json'}}, options.timeout); }; post: HTTPMethod = (url, options = {}) => { - return this.request(url, {...options, method: METHODS.POST}, options.timeout); + return this.request(this.baseUrl+url, {...options, method: METHODS.POST,headers:{'Content-Type': 'application/json'}}, options.timeout); }; delete: HTTPMethod = (url, options = {}) => { - return this.request(url, {...options, method: METHODS.DELETE}, options.timeout); + return this.request(this.baseUrl+url, {...options, method: METHODS.DELETE}, options.timeout); }; request = (url: string, options: IOptionsRequest = {method: METHODS.GET}, timeout = 5000) => { @@ -48,8 +52,8 @@ class HTTPTransport { const xhr = new XMLHttpRequest(); xhr.timeout = timeout; - const isGet = method === METHODS.GET; - xhr.open(method || METHODS.GET, isGet ? `${url}${data}` : url,); + //const isGet = method === METHODS.GET; + xhr.open(method || METHODS.GET, url); if (headers) { From ad2820baef5707e36119d73286ef0c62d05c225a Mon Sep 17 00:00:00 2001 From: ellevanova Date: Thu, 5 Oct 2023 21:23:49 +0400 Subject: [PATCH 06/43] added auth users with page --- src/api/auth.ts | 20 ++++------ src/components/button/button.pcss | 12 +++++- src/components/form-auth/form-auth.ts | 8 ++-- src/components/form-profile/form-profile.ts | 13 +++++-- src/config.ts | 12 ++++++ src/main.ts | 22 +++++------ src/pages/login/login.ts | 41 +++++++++++++++------ src/pages/profile/profile.ts | 2 +- src/pages/registration/registration.ts | 17 +++++++-- src/services/auth.ts | 39 +++++++++++++++++--- src/utils/Http.ts | 3 +- src/utils/api.utils.ts | 16 ++++++++ 12 files changed, 149 insertions(+), 56 deletions(-) create mode 100644 src/utils/api.utils.ts diff --git a/src/api/auth.ts b/src/api/auth.ts index 39419e192..1d7411277 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -9,22 +9,18 @@ export class AuthApi { if (baseUrl) this.baseUrl = baseUrl; } - public async signUp(userData: IUser) { - const result = await this.httpTransport.post(this.baseUrl + '/signup', {data: userData}); - console.log(result); + public signUp(userData: IUser) { + return this.httpTransport.post(this.baseUrl + '/signup', {data: userData}); } - public async signIn(userData: IAuthData) { - const result = await this.httpTransport.post(this.baseUrl + '/signin', {data: userData}); - console.log(result); + public signIn(userData: IAuthData) { + return this.httpTransport.post(this.baseUrl + '/signin', {data: userData}); } - public async getAuthUser() { - const result = await this.httpTransport.get(this.baseUrl + '/user'); - console.log(result); + public getAuthUser() { + return this.httpTransport.get(this.baseUrl + '/user'); } - public async logOut() { - const result = await this.httpTransport.post(this.baseUrl + '/logout'); - console.log(result); + public logOut() { + return this.httpTransport.post(this.baseUrl + '/logout'); } } diff --git a/src/components/button/button.pcss b/src/components/button/button.pcss index 08dd1c206..5611d4438 100644 --- a/src/components/button/button.pcss +++ b/src/components/button/button.pcss @@ -69,5 +69,13 @@ height: 25px; } - - +.button-link { + padding: 5px; + background-color: transparent; + color: var(--red); + text-transform: none; + width: auto; + &:hover { + text-decoration: underline; + } +} diff --git a/src/components/form-auth/form-auth.ts b/src/components/form-auth/form-auth.ts index 0616c071a..74ab6cc3f 100644 --- a/src/components/form-auth/form-auth.ts +++ b/src/components/form-auth/form-auth.ts @@ -8,10 +8,8 @@ interface IFormAuthProps extends IProps { onClickCancelButton: (event:Event) => void, captionOk: string, captionCancel: string, - pageOk:string, - pageCancel:string, validate:IValidateType, - onClickOk:(event:Event) => void, + cancelLink:string } export class FormAuth extends Block { constructor(props:IFormAuthProps) { @@ -24,7 +22,7 @@ export class FormAuth extends Block { } protected render(): string { - const {caption='Login',children='',onClickCancelButton,captionOk,captionCancel,pageCancel}=this._props as IFormAuthProps; + const {caption='Login',children='',captionOk,captionCancel,cancelLink}=this._props as IFormAuthProps; return(`

@@ -35,7 +33,7 @@ export class FormAuth extends Block {

{{{ Button caption="${captionOk}" onClick=onClickOkButton }}} - {{{ Link caption="${captionCancel}" page="${pageCancel}" onClick=${onClickCancelButton} }}} + {{{ Link caption="${captionCancel}" href="${cancelLink}" }}}
`) diff --git a/src/components/form-profile/form-profile.ts b/src/components/form-profile/form-profile.ts index 89e3adbbf..588c536a2 100644 --- a/src/components/form-profile/form-profile.ts +++ b/src/components/form-profile/form-profile.ts @@ -1,6 +1,7 @@ import {IProps,Block} from "../../utils/Block"; import {IUser} from "../../models/IUser.ts"; import {ALL_VALIDATE_FIELDS, IValidateType} from "../../models/IValidateType.ts"; +import {logOut} from "../../services/auth.ts"; interface IFormProfileProps extends IProps{ user:IUser, @@ -10,19 +11,23 @@ interface IFormProfileProps extends IProps{ buttonPage:string, buttonCancelPage:string, onClickOkButton: (event:Event) => void, + onLogOut: (event:Event) => void, validate:IValidateType, } export class FormProfile extends Block { constructor(props:IFormProfileProps) { props.validate= ALL_VALIDATE_FIELDS; + props.onLogOut=(event: Event) => { + event.preventDefault(); + logOut().catch((error)=>console.log(error)) + } super(props); } protected render(): string { - const {user,withButton=false,children='',buttonText='', - buttonCancelPage=''}=this._props as IFormProfileProps; + const {user,withButton=false,children='',buttonText=''}=this._props as IFormProfileProps; const {avatar,first_name,second_name}=user; return(` @@ -44,11 +49,11 @@ export class FormProfile extends Block { `
{{{Link caption="Change IUser Data" href='/settings-edit' type='success' linkLine=true }}} {{{Link caption="Change Password" href='/password-edit' type='success' linkLine=true }}} - {{{Link caption="Cancel" href='/messenger' type='danger' }}} + {{{Button caption="Logout" onClick=onLogOut type='link' }}}
`}
- {{{ Button type="cancel" page="${buttonCancelPage}" }}} + {{{ Button type="cancel" }}}
`) } diff --git a/src/config.ts b/src/config.ts index 6d6590997..176239da8 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,2 +1,14 @@ export const urlImages = '/'; export const BASE_API_URL='https://ya-praktikum.tech/api/v2'; +export const BASE_URLS={ + 'page-login':"/login", + 'page-sign-up':"/sign-up", + 'page-profile':"/settings", + 'page-profile-edit':"/settings-edit", + 'page-password-edit':"/password-edit", + 'page-404':"/404", + 'page-500':"/500", + 'page-chat':"/messenger", + 'page-default':"/", + 'page-all-components':"/story-book", +} diff --git a/src/main.ts b/src/main.ts index 8ed8e8c3d..fa8ceabaa 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,6 +4,7 @@ import * as Pages from './pages'; import {registerComponent} from "./utils/registerComponents"; import Router from "./utils/Router.ts"; import Block from "./utils/Block.ts"; +import {BASE_URLS} from "./config.ts"; @@ -33,15 +34,14 @@ Object.entries(allComponents).forEach(([name, component]) => { const router = new Router(".app"); -// Можно обновиться на /user и получить сразу пользователя -router.use("/", Pages.LoginPage as unknown as Block) - .use("/all", Pages.AllComponentsPage as unknown as Block) - .use("/login", Pages.LoginPage as unknown as Block) - .use("/sign-up", Pages.PageRegistration as unknown as Block) - .use("/settings", Pages.PageProfile as unknown as Block) - .use("/settings-edit", Pages.PageProfileEdit as unknown as Block) - .use("/password-edit", Pages.PagePasswordEdit as unknown as Block) - .use("/404", Pages.Page404 as unknown as Block) - .use("/500", Pages.Page500 as unknown as Block) - .use("/messenger", Pages.PageChat as unknown as Block) +router.use(BASE_URLS['page-default'], Pages.LoginPage as unknown as Block) + .use(BASE_URLS['page-all-components'], Pages.AllComponentsPage as unknown as Block) + .use(BASE_URLS['page-login'], Pages.LoginPage as unknown as Block) + .use(BASE_URLS['page-sign-up'], Pages.PageRegistration as unknown as Block) + .use(BASE_URLS['page-profile'], Pages.PageProfile as unknown as Block) + .use(BASE_URLS['page-profile-edit'], Pages.PageProfileEdit as unknown as Block) + .use(BASE_URLS['page-password-edit'], Pages.PagePasswordEdit as unknown as Block) + .use(BASE_URLS['page-404'], Pages.Page404 as unknown as Block) + .use(BASE_URLS['page-500'], Pages.Page500 as unknown as Block) + .use(BASE_URLS['page-chat'], Pages.PageChat as unknown as Block) .start(); diff --git a/src/pages/login/login.ts b/src/pages/login/login.ts index 545de57d2..78ec8aa63 100644 --- a/src/pages/login/login.ts +++ b/src/pages/login/login.ts @@ -1,37 +1,54 @@ -import {IProps,Block} from "../../utils/Block"; -import Router from "../../utils/Router.ts"; +import {IProps, Block} from "../../utils/Block"; +import {BASE_URLS} from "../../config.ts"; +import {getUser, signIn} from "../../services/auth.ts"; export interface ILoginPageProps extends IProps { - onLogin:(event:Event)=>void + onLogin: (event: Event) => void; } + export class LoginPage extends Block { constructor() { - const props:ILoginPageProps={ - events:{}, - onLogin: (event:Event) => { + const props: ILoginPageProps = { + events: {}, + onLogin: (event: Event) => { event.preventDefault(); - const login = this.refs.formLogin.getRefs()?.login.value(); - const password = this.refs.formLogin.getRefs()?.password.value(); + const login = this.refs.formLogin.getRefs()?.login.value(); + const password = this.refs.formLogin.getRefs()?.password.value(); console.log({ login, password }) - if(login&&password)Router.getRouter().go('/messenger'); + if (!login) { + return; + } + if (!password) { + return; + } + signIn({login, password}).catch((error)=>console.log(error)) } } super(props); + getUser().catch((error)=>console.log(error)) } protected render(): string { - const children:string=` + const children: string = ` {{{ InputShort label='Login' type='text' name='login' validate=validate.login ref='login' }}} {{{ InputShort label='Password' type='password' name='password' validate=validate.password ref='password' }}}` - return(` + return (`
- {{{ FormAuth caption="Login" captionOk="Login" captionCancel="Cancel" pageOk="allPages" pageCancel="loginPage" onClickOkButton=onLogin children="${children}" ref="formLogin" }}} + {{{ FormAuth + caption="Login" + captionOk="Login" + captionCancel="Sign up" + onClickOkButton=onLogin + children="${children}" + ref="formLogin" + cancelLink="${BASE_URLS['page-sign-up']}" + }}}
`) } } diff --git a/src/pages/profile/profile.ts b/src/pages/profile/profile.ts index de9640148..b146dfd2c 100644 --- a/src/pages/profile/profile.ts +++ b/src/pages/profile/profile.ts @@ -31,7 +31,7 @@ export class PageProfile extends Block { protected render(): string { return (`
- {{{ FormProfile user=user withButton=false children="${this.getChildren()}" ref="form" buttonPage='pageChat' }}} + {{{ FormProfile user=user withButton=false children="${this.getChildren()}" ref="form" }}}
`) } } diff --git a/src/pages/registration/registration.ts b/src/pages/registration/registration.ts index 5ab04af75..e21180b3c 100644 --- a/src/pages/registration/registration.ts +++ b/src/pages/registration/registration.ts @@ -1,6 +1,7 @@ import {IProps,Block} from "../../utils/Block.ts"; -import {signup} from "../../services/auth.ts"; +import {signUp} from "../../services/auth.ts"; import {IUser} from "../../models/IUser.ts"; +import {BASE_URLS} from "../../config.ts"; export interface IPageRegistrationProps extends IProps { onLogin:(event:Event)=>void, @@ -38,7 +39,7 @@ export class PageRegistration extends Block { password, phone } as IUser; - signup(data) + signUp(data) } } @@ -64,7 +65,17 @@ export class PageRegistration extends Block { return (`
- {{{ FormAuth caption="Registration" captionOk="sign up" captionCancel="Cancel" pageOk="allPages" pageCancel="loginPage" onClickOkButton=onLogin children="${this.getChildren()}" ref="form" }}} + {{{ FormAuth + caption="Registration" + captionOk="sign up" + captionCancel="Cancel" + pageOk="allPages" + pageCancel="loginPage" + onClickOkButton=onLogin + children="${this.getChildren()}" + ref="form" + cancelLink="${BASE_URLS['page-login']}" + }}}
`) } } diff --git a/src/services/auth.ts b/src/services/auth.ts index bee6d3d46..3fed5793b 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -1,13 +1,42 @@ -import {IUser} from "../models/IUser.ts"; +import {IAuthData, IUser} from "../models/IUser.ts"; import AuthApi from "../api/auth.ts"; +import {responseHasError} from "../utils/api.utils.ts"; +import Router from "../utils/Router.ts"; +import {BASE_URLS} from "../config.ts"; -const authApi=new AuthApi('/auth'); -const signup = async (data: IUser) => { - await authApi.signUp(data); +const authApi=new AuthApi('/auth'); +const signUp = async (data: IUser) => { + const result= await authApi.signUp(data); + console.log(result) +} +const signIn= async (data: IAuthData) => { + const result=await authApi.signIn(data); + const error=responseHasError(result as XMLHttpRequest); + if(error) throw Error(error); + await getUser(); +} +const getUser= async () => { + const result=await authApi.getAuthUser() as XMLHttpRequest; + const error=responseHasError(result); + if(error) throw Error(error); + // @ts-ignore + window.user=JSON.parse( result.responseText); + Router.getRouter().go(BASE_URLS['page-chat']); +} +const logOut= async () => { + const result=await authApi.logOut() as XMLHttpRequest; + const error=responseHasError(result); + if(error) throw Error(error); + // @ts-ignore + window.user=null; + Router.getRouter().go(BASE_URLS['page-login']); } export { - signup, + signUp, + signIn, + getUser, + logOut } diff --git a/src/utils/Http.ts b/src/utils/Http.ts index 6c0406d15..7764cfa96 100644 --- a/src/utils/Http.ts +++ b/src/utils/Http.ts @@ -45,12 +45,13 @@ class HTTPTransport { return this.request(this.baseUrl+url, {...options, method: METHODS.DELETE}, options.timeout); }; - request = (url: string, options: IOptionsRequest = {method: METHODS.GET}, timeout = 5000) => { + request = (url: string, options: IOptionsRequest = {method: METHODS.GET,}, timeout = 5000) => { const {method, headers, data} = options; return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); + xhr.withCredentials = true; xhr.timeout = timeout; //const isGet = method === METHODS.GET; xhr.open(method || METHODS.GET, url); diff --git a/src/utils/api.utils.ts b/src/utils/api.utils.ts new file mode 100644 index 000000000..d459f570e --- /dev/null +++ b/src/utils/api.utils.ts @@ -0,0 +1,16 @@ +import {BASE_URLS} from "../config.ts"; +import Router from "./Router.ts"; + + + +export const responseHasError=(response:XMLHttpRequest)=>{ + switch (response.status) { + case 200:return false; + case 500: + Router.getRouter().go(BASE_URLS['page-500']); + break; + default: + return JSON.parse( response.responseText).reason; + + } +} From 75dc9a7cd97789b74a3a85df0a74aee00280a2d7 Mon Sep 17 00:00:00 2001 From: ellevanova Date: Fri, 6 Oct 2023 16:18:37 +0400 Subject: [PATCH 07/43] added store user?fixed errors --- src/components/form-profile/form-profile.ts | 18 +++++++++----- src/components/link/link.ts | 14 ++++++++--- src/main.ts | 14 ++++++++++- src/pages/chat-page/page-chat.ts | 5 ++++ src/pages/login/login.ts | 4 ++-- src/pages/password-edit/password-edit.ts | 5 ++-- src/pages/profile-edit/profile-edit.ts | 14 ++++++----- src/pages/profile/profile.ts | 26 ++++++++++++++------- src/services/app.ts | 25 ++++++++++++++++++++ src/services/auth.ts | 9 ++++--- tsconfig.json | 4 +++- 11 files changed, 102 insertions(+), 36 deletions(-) create mode 100644 src/services/app.ts diff --git a/src/components/form-profile/form-profile.ts b/src/components/form-profile/form-profile.ts index 588c536a2..c50a8f2f8 100644 --- a/src/components/form-profile/form-profile.ts +++ b/src/components/form-profile/form-profile.ts @@ -4,7 +4,7 @@ import {ALL_VALIDATE_FIELDS, IValidateType} from "../../models/IValidateType.ts" import {logOut} from "../../services/auth.ts"; interface IFormProfileProps extends IProps{ - user:IUser, + user:IUser|null, withButton:boolean, children: string, buttonText:string, @@ -16,25 +16,31 @@ interface IFormProfileProps extends IProps{ } export class FormProfile extends Block { + constructor(props:IFormProfileProps) { + props.validate= ALL_VALIDATE_FIELDS; props.onLogOut=(event: Event) => { event.preventDefault(); logOut().catch((error)=>console.log(error)) } + props.user=window.user; + console.log(props) super(props); } - + public get props(){ + return this._props as IFormProfileProps; + } protected render(): string { - const {user,withButton=false,children='',buttonText=''}=this._props as IFormProfileProps; - const {avatar,first_name,second_name}=user; - + const {user,withButton=false,children='',buttonText=''}=this.props; + if(!user)return ''; + const {avatar='',first_name,second_name}=user; return(`
- {{{ Avatar image=${avatar} isLoadAvatar=true }}} + {{{ Avatar imageUrl=${avatar} isLoadAvatar=true }}}

${first_name} ${second_name}

${user ? diff --git a/src/components/link/link.ts b/src/components/link/link.ts index 198384f4c..0dbfe8981 100644 --- a/src/components/link/link.ts +++ b/src/components/link/link.ts @@ -1,4 +1,5 @@ import {IProps,Block} from "../../utils/Block"; +import Router from "../../utils/Router.ts"; interface ILinkProps extends IProps{ caption: string, @@ -11,16 +12,23 @@ interface ILinkProps extends IProps{ export class Link extends Block { constructor(props: ILinkProps) { - super(props); + super({ + ...props, + events: { + click: ()=>{ + Router.getRouter().go(props.href||'/'); + } + } + }) } public get props(){ return this._props as ILinkProps; } protected render(): string { - const { href='#', caption='', page='' ,linkIcon=false,linkLine=false,type=''} = this.props; + const { caption='', page='' ,linkIcon=false,linkLine=false,type=''} = this.props; const classLink=`link ${type?`link-${type}`:''} ${linkLine?'link-line':''}` return (` - ${caption} diff --git a/src/main.ts b/src/main.ts index fa8ceabaa..c674467c8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,6 +5,9 @@ import {registerComponent} from "./utils/registerComponents"; import Router from "./utils/Router.ts"; import Block from "./utils/Block.ts"; import {BASE_URLS} from "./config.ts"; +import {IUser} from "./models/IUser.ts"; +import {initialStateApp} from "./services/app.ts"; + @@ -31,10 +34,19 @@ Object.entries(allComponents).forEach(([name, component]) => { registerComponent(name, component); }); +declare global { + interface Window { + user: IUser|null; + } + + type Nullable = T | null; + +} +await initialStateApp(); const router = new Router(".app"); -router.use(BASE_URLS['page-default'], Pages.LoginPage as unknown as Block) +router.use(BASE_URLS['page-default'], Pages.PageChat as unknown as Block) .use(BASE_URLS['page-all-components'], Pages.AllComponentsPage as unknown as Block) .use(BASE_URLS['page-login'], Pages.LoginPage as unknown as Block) .use(BASE_URLS['page-sign-up'], Pages.PageRegistration as unknown as Block) diff --git a/src/pages/chat-page/page-chat.ts b/src/pages/chat-page/page-chat.ts index 880cb1ed3..f24b90fb9 100644 --- a/src/pages/chat-page/page-chat.ts +++ b/src/pages/chat-page/page-chat.ts @@ -5,6 +5,8 @@ import {mockListMessages} from "../../mocks/chat-message.mocks.ts"; import {IChat} from "../../models/IChat.ts"; import {IUser} from "../../models/IUser.ts"; import {IChatMessage} from "../../models/IChatMessage.ts"; +import Router from "../../utils/Router.ts"; +import {BASE_URLS} from "../../config.ts"; export interface IPageChatProps extends IProps { currentUser:IUser, @@ -12,7 +14,9 @@ export interface IPageChatProps extends IProps { messageList:IChatMessage[], } export class PageChat extends Block { + constructor() { + console.log('window.user', window.user) const props:IPageChatProps={ currentUser:mockUser, chatList:mockListChats, @@ -20,6 +24,7 @@ export class PageChat extends Block { events:{} } super(props); + if(!window.user) Router.getRouter().go(BASE_URLS['page-login']); } protected render(): string { diff --git a/src/pages/login/login.ts b/src/pages/login/login.ts index 78ec8aa63..7896a92d5 100644 --- a/src/pages/login/login.ts +++ b/src/pages/login/login.ts @@ -29,9 +29,9 @@ export class LoginPage extends Block { signIn({login, password}).catch((error)=>console.log(error)) } } - + getUser().catch(error=>console.log(error)) super(props); - getUser().catch((error)=>console.log(error)) + console.log('window.user', window.user) } protected render(): string { diff --git a/src/pages/password-edit/password-edit.ts b/src/pages/password-edit/password-edit.ts index 2c3a38e3b..4ee8141ea 100644 --- a/src/pages/password-edit/password-edit.ts +++ b/src/pages/password-edit/password-edit.ts @@ -1,17 +1,16 @@ import {IProps,Block} from "../../utils/Block.ts"; -import {mockUser} from "../../mocks/user-profile.mocks.ts"; import {IUser} from "../../models/IUser.ts"; export interface ILoginPageProps extends IProps { onChange:(event:Event)=>void, - user:IUser + user:IUser|null } export class PagePasswordEdit extends Block { constructor() { const props:ILoginPageProps={ events:{}, - user:mockUser, + user:window.user, onChange: (event: Event) => { event.preventDefault(); diff --git a/src/pages/profile-edit/profile-edit.ts b/src/pages/profile-edit/profile-edit.ts index 24a41f922..ab241e44d 100644 --- a/src/pages/profile-edit/profile-edit.ts +++ b/src/pages/profile-edit/profile-edit.ts @@ -1,17 +1,16 @@ import {IProps,Block} from "../../utils/Block.ts"; -import {mockUser} from "../../mocks/user-profile.mocks.ts"; import {IUser} from "../../models/IUser.ts"; import {IPageProfileProps} from "../profile/profile.ts"; export interface IPageProfileEditProps extends IProps { onChange:(event:Event)=>void, - user:IUser + user:IUser|null } export class PageProfileEdit extends Block { constructor() { const props:IPageProfileEditProps={ events:{}, - user:mockUser, + user:window.user, onChange: (event: Event) => { event.preventDefault(); const login = this.refs.form.getRefs()?.login.value(); @@ -33,15 +32,18 @@ export class PageProfileEdit extends Block { } super(props); } - + public get props(){ + return this._props as IPageProfileProps; + } getChildren() { - const {email,login,first_name,second_name,display_name,phone}=(this._props as IPageProfileProps).user; + if(!this.props.user) return ''; + const {email,login,first_name,second_name,display_name,phone}=this.props.user; return ( `{{{ InputWide label='Email' type='email' name='email' validate=validate.email ref='email' readOnly=false value='${email}' }}} {{{ InputWide label='Login' type='text' name='login' validate=validate.login ref='login' readOnly=false value='${login}' }}} {{{ InputWide label='First Name' type='first_name' name='first_name' validate=validate.name ref='first_name' readOnly=false value='${first_name}' }}} {{{ InputWide label='Second Name' name='second_name' validate=validate.name ref='second_name' readOnly=false value='${second_name}' }}} - {{{ InputWide label='Name in Chat' name='display_name' validate=validate.name ref='display_name' readOnly=false value='${display_name}' }}} + {{{ InputWide label='Name in Chat' name='display_name' validate=validate.name ref='display_name' readOnly=false value='${display_name||''}' }}} {{{ InputWide label='Phone' name='phone' validate=validate.phone ref='phone' readOnly=false value='${phone}' }}} ` diff --git a/src/pages/profile/profile.ts b/src/pages/profile/profile.ts index b146dfd2c..15eceed28 100644 --- a/src/pages/profile/profile.ts +++ b/src/pages/profile/profile.ts @@ -1,37 +1,45 @@ import {IProps,Block} from "../../utils/Block.ts"; -import {mockUser} from "../../mocks/user-profile.mocks.ts"; import {IUser} from "../../models/IUser.ts"; export interface IPageProfileProps extends IProps { - user:IUser + user:IUser|null } export class PageProfile extends Block { + constructor() { + console.log('window.user', window.user) const props:IPageProfileProps= { - events: {}, - user: mockUser, + user: window.user, + events: {} } + super(props); } getChildren() { - const {email,login,first_name,second_name,display_name,phone}=(this._props as IPageProfileProps).user; + const user=(this._props as IPageProfileProps).user; + if(!user )return '' + const {email,login,first_name,second_name,display_name,phone}=user; return ( `{{{ InputWide label='Email' type='email' name='email' validate=validate.email ref='email' readOnly=true value='${email}' }}} {{{ InputWide label='Login' type='text' name='login' validate=validate.login ref='login' readOnly=true value='${login}' }}} {{{ InputWide label='First Name' type='first_name' name='first_name' validate=validate.name ref='first_name' readOnly=true value='${first_name}' }}} {{{ InputWide label='Second Name' name='second_name' validate=validate.name ref='second_name' readOnly=true value='${second_name}' }}} - {{{ InputWide label='Name in Chat' name='display_name' validate=validate.name ref='display_name' readOnly=true value='${display_name}' }}} - {{{ InputWide label='Phone' name='phone' validate=validate.phone ref='phone' readOnly=true value='${phone}' }}} - + {{{ InputWide label='Name in Chat' name='display_name' validate=validate.name ref='display_name' readOnly=true value='${display_name||''}' }}} + {{{ InputWide label='Phone' name='phone' validate=validate.phone ref='phone' readOnly=true value='${phone}' }}} ` ) + } protected render(): string { - return (` + if((this._props as IPageProfileProps).user){ + return (`
{{{ FormProfile user=user withButton=false children="${this.getChildren()}" ref="form" }}}
`) + } + else return '' + } } diff --git a/src/services/app.ts b/src/services/app.ts new file mode 100644 index 000000000..e381d4fa8 --- /dev/null +++ b/src/services/app.ts @@ -0,0 +1,25 @@ +import {getUser} from "./auth.ts"; +import Router from "../utils/Router.ts"; +import {BASE_URLS} from "../config.ts"; + +const initialStateApp = async () => { + + let result = null; + try { + result = await getUser(); + } catch (error) { + Router.getRouter().go(BASE_URLS['page-login']); + return; + } + window.user=result; + console.log('window.user_initial', window.user,Router.getRouter()) + /*const chats = await getChats(); + window.store.set({user: me, chats}); + navigate('emails')*/ + +} + + +export{ + initialStateApp +} diff --git a/src/services/auth.ts b/src/services/auth.ts index 3fed5793b..768ccea48 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -3,6 +3,7 @@ import AuthApi from "../api/auth.ts"; import {responseHasError} from "../utils/api.utils.ts"; import Router from "../utils/Router.ts"; import {BASE_URLS} from "../config.ts"; +import {initialStateApp} from "./app.ts"; const authApi=new AuthApi('/auth'); @@ -14,22 +15,20 @@ const signIn= async (data: IAuthData) => { const result=await authApi.signIn(data); const error=responseHasError(result as XMLHttpRequest); if(error) throw Error(error); - await getUser(); + await initialStateApp(); } const getUser= async () => { const result=await authApi.getAuthUser() as XMLHttpRequest; const error=responseHasError(result); if(error) throw Error(error); - // @ts-ignore - window.user=JSON.parse( result.responseText); - Router.getRouter().go(BASE_URLS['page-chat']); + return JSON.parse( result.responseText); + } const logOut= async () => { const result=await authApi.logOut() as XMLHttpRequest; const error=responseHasError(result); if(error) throw Error(error); - // @ts-ignore window.user=null; Router.getRouter().go(BASE_URLS['page-login']); } diff --git a/tsconfig.json b/tsconfig.json index 0c9fccf83..c083587e9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,7 +18,9 @@ "noUnusedLocals": true, "noUnusedParameters": true, "sourceMap": true, - "strictNullChecks": true + "strictNullChecks": true, + + "typeRoots": ["node_modules/@types", "models"] }, "include": ["src"] } From b8f21ad533d0a4c1998b00a1c44ec1f5bd45a0e3 Mon Sep 17 00:00:00 2001 From: ellevanova Date: Fri, 6 Oct 2023 17:30:33 +0400 Subject: [PATCH 08/43] added user settings api --- src/api/user-settings.ts | 26 +++++++++++++++++++++ src/models/IUser.ts | 5 ++++ src/pages/password-edit/password-edit.ts | 9 ++++---- src/pages/profile-edit/profile-edit.ts | 8 ++++--- src/pages/profile/profile.ts | 1 - src/services/app.ts | 9 ++++++-- src/services/user-settings.ts | 29 ++++++++++++++++++++++++ src/utils/Http.ts | 14 ++++++------ 8 files changed, 83 insertions(+), 18 deletions(-) create mode 100644 src/api/user-settings.ts create mode 100644 src/services/user-settings.ts diff --git a/src/api/user-settings.ts b/src/api/user-settings.ts new file mode 100644 index 000000000..875bdb542 --- /dev/null +++ b/src/api/user-settings.ts @@ -0,0 +1,26 @@ +import HTTPTransport from "../utils/Http.ts"; +import {IPasswords, IUser} from "../models/IUser.ts"; + +export class UserSettingsApi { + private httpTransport = new HTTPTransport(); + private readonly baseUrl: string | null = null; + + constructor(baseUrl?: string) { + if (baseUrl) this.baseUrl = baseUrl; + } + + public changeUserProfile(userData: IUser) { + return this.httpTransport.put(this.baseUrl + '/profile', {data: userData}); + } + + public changeUserAvatar(file: FormData) { + return this.httpTransport.put(this.baseUrl + '/profile/avatar', {data: file}); + } + + public changeUserPassword(data: IPasswords) { + return this.httpTransport.put(this.baseUrl + '/password', {data: data}); + } + +} + +export default UserSettingsApi diff --git a/src/models/IUser.ts b/src/models/IUser.ts index 1fe9034f3..132d6a722 100644 --- a/src/models/IUser.ts +++ b/src/models/IUser.ts @@ -13,3 +13,8 @@ export interface IAuthData { login: string; password: string; } + +export interface IPasswords { + oldPassword: string, + newPassword: string +} diff --git a/src/pages/password-edit/password-edit.ts b/src/pages/password-edit/password-edit.ts index 4ee8141ea..801e355b7 100644 --- a/src/pages/password-edit/password-edit.ts +++ b/src/pages/password-edit/password-edit.ts @@ -1,5 +1,6 @@ import {IProps,Block} from "../../utils/Block.ts"; import {IUser} from "../../models/IUser.ts"; +import {updateUserPassword} from "../../services/user-settings.ts"; export interface ILoginPageProps extends IProps { onChange:(event:Event)=>void, @@ -11,17 +12,15 @@ export class PagePasswordEdit extends Block { const props:ILoginPageProps={ events:{}, user:window.user, - onChange: (event: Event) => { - + onChange: async (event: Event) => { event.preventDefault(); const oldPassword = this.refs.form.getRefs()?.oldPassword.value(); const newPassword = this.refs.form.getRefs()?.newPassword.value(); const repeatPassword = this.refs.form.getRefs()?.repeatPassword.value(); - console.log({ + if (oldPassword && newPassword && newPassword === repeatPassword) await updateUserPassword({ oldPassword, - newPassword, - repeatPassword, + newPassword }) } } diff --git a/src/pages/profile-edit/profile-edit.ts b/src/pages/profile-edit/profile-edit.ts index ab241e44d..d651173f9 100644 --- a/src/pages/profile-edit/profile-edit.ts +++ b/src/pages/profile-edit/profile-edit.ts @@ -1,6 +1,7 @@ import {IProps,Block} from "../../utils/Block.ts"; import {IUser} from "../../models/IUser.ts"; import {IPageProfileProps} from "../profile/profile.ts"; +import {updateUserProfile} from "../../services/user-settings.ts"; export interface IPageProfileEditProps extends IProps { onChange:(event:Event)=>void, @@ -11,7 +12,7 @@ export class PageProfileEdit extends Block { const props:IPageProfileEditProps={ events:{}, user:window.user, - onChange: (event: Event) => { + onChange: async (event: Event) => { event.preventDefault(); const login = this.refs.form.getRefs()?.login.value(); const email = this.refs.form.getRefs()?.email.value(); @@ -20,14 +21,15 @@ export class PageProfileEdit extends Block { const second_name = this.refs.form.getRefs()?.second_name.value(); const display_name = this.refs.form.getRefs()?.display_name.value(); - console.log({ + const userData: IUser = { login, second_name, first_name, display_name, phone, email - }) + } + if (login && first_name && second_name && phone && email) await updateUserProfile(userData); } } super(props); diff --git a/src/pages/profile/profile.ts b/src/pages/profile/profile.ts index 15eceed28..b23c9d65e 100644 --- a/src/pages/profile/profile.ts +++ b/src/pages/profile/profile.ts @@ -7,7 +7,6 @@ export interface IPageProfileProps extends IProps { export class PageProfile extends Block { constructor() { - console.log('window.user', window.user) const props:IPageProfileProps= { user: window.user, events: {} diff --git a/src/services/app.ts b/src/services/app.ts index e381d4fa8..306a44699 100644 --- a/src/services/app.ts +++ b/src/services/app.ts @@ -1,6 +1,7 @@ import {getUser} from "./auth.ts"; import Router from "../utils/Router.ts"; import {BASE_URLS} from "../config.ts"; +import {IUser} from "../models/IUser.ts"; const initialStateApp = async () => { @@ -11,7 +12,7 @@ const initialStateApp = async () => { Router.getRouter().go(BASE_URLS['page-login']); return; } - window.user=result; + setStateUser(result); console.log('window.user_initial', window.user,Router.getRouter()) /*const chats = await getChats(); window.store.set({user: me, chats}); @@ -19,7 +20,11 @@ const initialStateApp = async () => { } +const setStateUser=(user:IUser)=>{ + window.user=user; +} export{ - initialStateApp + initialStateApp, + setStateUser } diff --git a/src/services/user-settings.ts b/src/services/user-settings.ts new file mode 100644 index 000000000..1311d7d63 --- /dev/null +++ b/src/services/user-settings.ts @@ -0,0 +1,29 @@ +import UserSettingsApi from "../api/user-settings.ts"; +import {IPasswords, IUser} from "../models/IUser.ts"; +import {responseHasError} from "../utils/api.utils.ts"; +import {setStateUser} from "./app.ts"; +import Router from "../utils/Router.ts"; + + +const userApi=new UserSettingsApi('/user'); + +const updateUserProfile=async (newUserData: IUser) => { + const result= await userApi.changeUserProfile(newUserData); + const error=responseHasError(result); + if(error) throw Error(error); + setStateUser(JSON.parse(result.responseText)); + Router.getRouter().back(); +} +const updateUserPassword=async (newUserPasswords: IPasswords) => { + const result= await userApi.changeUserPassword(newUserPasswords); + const error=responseHasError(result); + if(error) throw Error(error); + Router.getRouter().back(); +} + + + +export { + updateUserProfile, + updateUserPassword +} diff --git a/src/utils/Http.ts b/src/utils/Http.ts index 7764cfa96..5f2c7a666 100644 --- a/src/utils/Http.ts +++ b/src/utils/Http.ts @@ -16,7 +16,7 @@ type IOptionsRequest = { params?: object; } -type HTTPMethod = (url: string, options?: IOptionsRequest) => Promise +type HTTPMethod = (url: string, options?: IOptionsRequest) => Promise @@ -25,24 +25,24 @@ class HTTPTransport { constructor(base_url?:string) { this.baseUrl=base_url||BASE_API_URL; } - get: HTTPMethod = (url, options = {}) => { + get: HTTPMethod = (url, options = {}):Promise => { return this.request(this.baseUrl+url+queryStringify(options.params || {}) || '', { ...options, method: METHODS.GET - }, options.timeout); + }, options.timeout)as Promise ; }; put: HTTPMethod = (url, options = {}) => { - return this.request(this.baseUrl+url, {...options, method: METHODS.PUT,headers:{'Content-Type': 'application/json'}}, options.timeout); + return this.request(this.baseUrl+url, {...options, method: METHODS.PUT,headers:{'Content-Type': 'application/json'}}, options.timeout) as Promise; }; - post: HTTPMethod = (url, options = {}) => { + post: HTTPMethod = (url, options = {})=> { - return this.request(this.baseUrl+url, {...options, method: METHODS.POST,headers:{'Content-Type': 'application/json'}}, options.timeout); + return this.request(this.baseUrl+url, {...options, method: METHODS.POST,headers:{'Content-Type': 'application/json'}}, options.timeout) as Promise; }; delete: HTTPMethod = (url, options = {}) => { - return this.request(this.baseUrl+url, {...options, method: METHODS.DELETE}, options.timeout); + return this.request(this.baseUrl+url, {...options, method: METHODS.DELETE}, options.timeout) as Promise; }; request = (url: string, options: IOptionsRequest = {method: METHODS.GET,}, timeout = 5000) => { From 5fb87dea0b3934ee1f58d433920837caf9fe763f Mon Sep 17 00:00:00 2001 From: ellevanova Date: Fri, 6 Oct 2023 23:20:41 +0400 Subject: [PATCH 09/43] added modal controller --- src/components/avatar/avatar.ts | 35 ++++++++++++++------ src/components/button/button.ts | 2 +- src/components/form-profile/form-profile.ts | 1 - src/components/modal/modal.pcss | 15 +++++++-- src/components/modal/modal.ts | 24 ++++++++------ src/index.html | 2 ++ src/utils/modal-controller.ts | 36 +++++++++++++++++++++ 7 files changed, 92 insertions(+), 23 deletions(-) create mode 100644 src/utils/modal-controller.ts diff --git a/src/components/avatar/avatar.ts b/src/components/avatar/avatar.ts index 77e9f8786..2c1eb9b54 100644 --- a/src/components/avatar/avatar.ts +++ b/src/components/avatar/avatar.ts @@ -1,6 +1,8 @@ -import {IProps,Block} from "../../utils/Block"; +import {IProps, Block} from "../../utils/Block"; +import modalController from "../../utils/modal-controller.ts"; +import {Modal} from "../index.ts"; -interface IAvatarProps extends IProps{ +interface IAvatarProps extends IProps { size: 'sm' | 'md', isLoadAvatar: boolean, onClickLoadAvatar: () => void, @@ -9,11 +11,25 @@ interface IAvatarProps extends IProps{ export class Avatar extends Block { constructor(props: IAvatarProps) { - super(props); - this._props.events = { - click: props.onClickLoadAvatar || (() => { - }) - } + super({ + ...props, + events: { + click: () => { + console.log('cancel') + modalController.openModal(); + } + } + }) + modalController.addModal((new Modal({ + okText: "Save", + cancelClick: () => { + console.log('cancel') + //modalController.closeModal() + }, + okClick: () => console.log('ok'), + cancelText: 'Cancel', + caption: 'Add Avatar' + })) as unknown as Block); } protected render(): string { @@ -24,8 +40,9 @@ export class Avatar extends Block { image avatar` : ``} ${isLoadAvatar ? `
-
Load New Avatar
+
Load New Avatar
` : ""} -
`) + + `) } } diff --git a/src/components/button/button.ts b/src/components/button/button.ts index f5114571d..a657d76a3 100644 --- a/src/components/button/button.ts +++ b/src/components/button/button.ts @@ -20,7 +20,7 @@ export class Button extends Block { protected render(): string { const { type='', caption='', page='' } = this._props as IButtonProps; return (` - diff --git a/src/components/form-profile/form-profile.ts b/src/components/form-profile/form-profile.ts index c50a8f2f8..0b7903072 100644 --- a/src/components/form-profile/form-profile.ts +++ b/src/components/form-profile/form-profile.ts @@ -26,7 +26,6 @@ export class FormProfile extends Block { } props.user=window.user; - console.log(props) super(props); } public get props(){ diff --git a/src/components/modal/modal.pcss b/src/components/modal/modal.pcss index 6792b0ba2..bc1c8bd0a 100644 --- a/src/components/modal/modal.pcss +++ b/src/components/modal/modal.pcss @@ -5,7 +5,6 @@ gap: 10px; padding: 30px; min-height: 300px; - width: 340px; z-index: 100; .modal__header { @@ -24,6 +23,19 @@ align-self: end; } } +.dialog::backdrop { + background-color: rgb(0 0 0 / 20%); + -webkit-backdrop-filter: blur(5px); + backdrop-filter: blur(5px); +} +.dialog { + border: none; + + width: 340px; + inset: 0; + position: fixed; + margin: auto; +} .modal-background { position: absolute; @@ -38,4 +50,3 @@ align-items: center; justify-content: center; } - diff --git a/src/components/modal/modal.ts b/src/components/modal/modal.ts index 6f95a366a..290553b2d 100644 --- a/src/components/modal/modal.ts +++ b/src/components/modal/modal.ts @@ -1,4 +1,4 @@ -import {IProps,Block} from "../../utils/Block"; +import {IProps, Block} from "../../utils/Block"; interface IModalProps extends IProps { caption: string, @@ -10,17 +10,22 @@ interface IModalProps extends IProps { export class Modal extends Block { constructor(props: IModalProps) { - super(props); + super({ + ...props + }) + this.props.okClick=() => { + console.log('cancel') + //modalController.closeModal() + } } - } - public get props(){ + public get props() { return this._props as IModalProps; } + protected render(): string { - const { caption='',okText='',cancelText=''} = this.props; + const {caption = '', okText = '', cancelText = ''} = this.props; return (` - + `) } } diff --git a/src/index.html b/src/index.html index bc931504c..36841769a 100644 --- a/src/index.html +++ b/src/index.html @@ -9,5 +9,7 @@
+ + diff --git a/src/utils/modal-controller.ts b/src/utils/modal-controller.ts new file mode 100644 index 000000000..402209b58 --- /dev/null +++ b/src/utils/modal-controller.ts @@ -0,0 +1,36 @@ +import Block from "./Block.ts"; + +class ModalController { + private static __instance: ModalController; + private dialog: HTMLDialogElement | null=null; + + constructor() { + if (ModalController.__instance) { + return ModalController.__instance; + } + ModalController.__instance = this; + this.dialog= document.getElementById('dialog') as HTMLDialogElement; + } + + public static getInstance() { + return this.__instance; + } + + public addModal(modal: Block) { + const htmlElement = modal.getContent(); + if (!this.dialog?.firstElementChild) this.dialog?.append(document.createElement('div')); + if(htmlElement)this.dialog?.firstElementChild?.replaceWith(htmlElement); + } + + public openModal(){ + console.log('OPEN!') + this.dialog?.showModal() + } + + public closeModal(){ + this.dialog?.close() + } + +} +const modalController=new ModalController(); +export default modalController; From eb466a250cc803d47412136d24233fda75c37b3f Mon Sep 17 00:00:00 2001 From: ellevanova Date: Sat, 7 Oct 2023 16:50:03 +0400 Subject: [PATCH 10/43] added change avatar modal --- src/components/avatar/avatar.ts | 16 +-- src/components/form-profile/form-profile.pcss | 1 - src/components/form-profile/form-profile.ts | 30 +++-- src/components/index.ts | 1 + src/components/modal-avatar/index.ts | 2 + src/components/modal-avatar/modal-avatar.pcss | 51 +++++++++ src/components/modal-avatar/modal-avatar.ts | 104 ++++++++++++++++++ src/components/modal/modal.ts | 14 +-- src/config.ts | 1 + src/main.ts | 1 + src/models/IUser.ts | 1 + src/services/user-settings.ts | 11 +- src/utils/Http.ts | 13 ++- src/utils/load-file.utils.ts | 32 ++++++ 14 files changed, 241 insertions(+), 37 deletions(-) create mode 100644 src/components/modal-avatar/index.ts create mode 100644 src/components/modal-avatar/modal-avatar.pcss create mode 100644 src/components/modal-avatar/modal-avatar.ts create mode 100644 src/utils/load-file.utils.ts diff --git a/src/components/avatar/avatar.ts b/src/components/avatar/avatar.ts index 2c1eb9b54..47af6a53a 100644 --- a/src/components/avatar/avatar.ts +++ b/src/components/avatar/avatar.ts @@ -1,6 +1,7 @@ import {IProps, Block} from "../../utils/Block"; import modalController from "../../utils/modal-controller.ts"; -import {Modal} from "../index.ts"; +import { ModalAvatar} from "../index.ts"; +import {BASE_RESOURCES_URL} from "../../config.ts"; interface IAvatarProps extends IProps { size: 'sm' | 'md', @@ -20,15 +21,8 @@ export class Avatar extends Block { } } }) - modalController.addModal((new Modal({ - okText: "Save", - cancelClick: () => { - console.log('cancel') - //modalController.closeModal() - }, - okClick: () => console.log('ok'), - cancelText: 'Cancel', - caption: 'Add Avatar' + modalController.addModal((new ModalAvatar({ + oldAvatar:window.user?.avatar||'' })) as unknown as Block); } @@ -37,7 +31,7 @@ export class Avatar extends Block { return (`
${imageUrl ? ` - image avatar` : ``} + image avatar` : ``} ${isLoadAvatar ? `
Load New Avatar
diff --git a/src/components/form-profile/form-profile.pcss b/src/components/form-profile/form-profile.pcss index 502f007f8..0be66a355 100644 --- a/src/components/form-profile/form-profile.pcss +++ b/src/components/form-profile/form-profile.pcss @@ -47,4 +47,3 @@ opacity: 0.8; } } - diff --git a/src/components/form-profile/form-profile.ts b/src/components/form-profile/form-profile.ts index 0b7903072..8b06aae0f 100644 --- a/src/components/form-profile/form-profile.ts +++ b/src/components/form-profile/form-profile.ts @@ -2,16 +2,16 @@ import {IProps,Block} from "../../utils/Block"; import {IUser} from "../../models/IUser.ts"; import {ALL_VALIDATE_FIELDS, IValidateType} from "../../models/IValidateType.ts"; import {logOut} from "../../services/auth.ts"; +import Router from "../../utils/Router.ts"; interface IFormProfileProps extends IProps{ user:IUser|null, withButton:boolean, children: string, buttonText:string, - buttonPage:string, - buttonCancelPage:string, onClickOkButton: (event:Event) => void, onLogOut: (event:Event) => void, + onCancel: (event:Event) => void, validate:IValidateType, } @@ -24,6 +24,10 @@ export class FormProfile extends Block { event.preventDefault(); logOut().catch((error)=>console.log(error)) } + props.onCancel=(event: Event) => { + event.preventDefault(); + Router.getRouter().back(); + } props.user=window.user; super(props); @@ -39,27 +43,29 @@ export class FormProfile extends Block {
- {{{ Avatar imageUrl=${avatar} isLoadAvatar=true }}} + {{{ Avatar imageUrl="${avatar}" isLoadAvatar=true }}}

${first_name} ${second_name}

${user ? `
${children} -
`:'' - } +
` : '' + } ${withButton ? - `
+ `
{{{ Button caption="${buttonText}" onClick=onClickOkButton }}} -
`: - `
+
` : + `
{{{Link caption="Change IUser Data" href='/settings-edit' type='success' linkLine=true }}} {{{Link caption="Change Password" href='/password-edit' type='success' linkLine=true }}} {{{Button caption="Logout" onClick=onLogOut type='link' }}} -
`} -
-
- {{{ Button type="cancel" }}} +
` + } +
+ {{{ Button type="cancel" onClick=onCancel}}}
+
+ `) } } diff --git a/src/components/index.ts b/src/components/index.ts index cb1712f80..627905dc2 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -7,6 +7,7 @@ export {default as InputWide } from './input-wide'; export { default as FormAuth } from './form-auth'; export {default as FormProfile } from './form-profile'; export {default as Modal } from './modal'; +export {default as ModalAvatar } from './modal-avatar'; export {default as Input } from './input'; export { default as ChatItem } from './chat-item'; export { default as InputSearch } from './input-search'; diff --git a/src/components/modal-avatar/index.ts b/src/components/modal-avatar/index.ts new file mode 100644 index 000000000..d4dc16e93 --- /dev/null +++ b/src/components/modal-avatar/index.ts @@ -0,0 +1,2 @@ +import './modal-avatar.pcss'; +export { ModalAvatar as default } from './modal-avatar'; diff --git a/src/components/modal-avatar/modal-avatar.pcss b/src/components/modal-avatar/modal-avatar.pcss new file mode 100644 index 000000000..3d48a61d2 --- /dev/null +++ b/src/components/modal-avatar/modal-avatar.pcss @@ -0,0 +1,51 @@ +.modal-avatar{ + display: flex; + flex-direction: column; + align-items: center; + position: relative; + z-index: 10; + border: 1px var(--white) solid; + border-radius: 20px; + padding: 20px 15px; + + &.highlight{ + border-color: var(--red-light); + } + + .modal-avatar__image{ + border-radius: 100px; + border: 1px var(--red) solid; + width: 100%; + height: 100%; + transition: .5s ease; + backface-visibility: hidden; + opacity: 1; + display: block; + height: 130px; + width: 130px; + + } + .modal-avatar__empty{ + background: url("../../assets/icons/image.svg") no-repeat center; + position: relative; + height: 130px; + width: 130px; + border-radius: 100px; + border: 1px var(--red) solid; + } + .modal-avatar__label { + margin-top:20px; + font-weight: bold; + &:hover { + cursor: pointer; + text-decoration: underline; + } + } + .modal-avatar__input{ + width: 0.1px; + height: 0.1px; + opacity: 0; + position: absolute; + z-index: -10; + } +} diff --git a/src/components/modal-avatar/modal-avatar.ts b/src/components/modal-avatar/modal-avatar.ts new file mode 100644 index 000000000..ce3516c3f --- /dev/null +++ b/src/components/modal-avatar/modal-avatar.ts @@ -0,0 +1,104 @@ +import {IProps, Block} from "../../utils/Block"; +import {updateUserAvatar, updateUserProfile} from "../../services/user-settings.ts"; +import {BASE_RESOURCES_URL} from "../../config.ts"; +import modalController from "../../utils/modal-controller.ts"; +import {addActive, deleteActive, loadNewFileFromDrag} from "../../utils/load-file.utils.ts"; + +interface IModalAvatarProps extends IProps { + oldAvatar?: string, + newAvatar?: string, + okClick?: () => void, + cancelClick?: () => void, + onAddFile?: (e: InputEvent) => void, + file?: unknown; +} + +export class ModalAvatar extends Block { + constructor(props: IModalAvatarProps) { + props.file = null; + props.newAvatar = ''; + props.okClick = () => { + modalController.closeModal(); + } + props.cancelClick = () => { + const user = window.user; + if (user && this.props.newAvatar) { + updateUserProfile({...user, avatar: this.props.oldAvatar}).then(() => { + modalController.closeModal(); + }); + } else modalController.closeModal(); + + } + + const _onAddFile = (e: TEvent) => { + deleteActive(e as Event); + const formData = loadNewFileFromDrag(e); + console.log('added file',e); + console.log('formData',formData) + if (formData) { + updateUserAvatar(formData).then(user => { + this.props.newAvatar = user.avatar; + modalController.addModal((new ModalAvatar({ + oldAvatar: window.user?.avatar || '' + })) as unknown as Block); + }); + } + } + super({ + ...props, + events: { + dragenter: (e: Event) => { + addActive(e); + }, + dragover: (e: Event) => { + addActive(e); + }, + dragleave: (e: Event) => { + deleteActive(e); + }, + drop: _onAddFile, + change:_onAddFile, + + } + }) + } + + + public get props() { + return this._props as IModalAvatarProps; + } + + getChildren() { + const {oldAvatar = '', newAvatar = ''} = this.props; + let result = ''; + if (newAvatar) { + result = `image avatar` + } else { + result = oldAvatar ? `image avatar` : `` + } + return ( + ` + ` + ) + } + + protected render(): string { + return (` + {{{ Modal + caption="Change Avatar" + okText='Save' + cancelText='Cancel' + okClick=okClick + cancelClick=cancelClick + children="${this.getChildren()}" + }}} + `) + } +} diff --git a/src/components/modal/modal.ts b/src/components/modal/modal.ts index 290553b2d..c04f237bf 100644 --- a/src/components/modal/modal.ts +++ b/src/components/modal/modal.ts @@ -6,6 +6,7 @@ interface IModalProps extends IProps { okClick: () => void, cancelText: string, cancelClick: () => void, + children?: string, } export class Modal extends Block { @@ -13,28 +14,25 @@ export class Modal extends Block { super({ ...props }) - this.props.okClick=() => { - console.log('cancel') - //modalController.closeModal() - } } + } public get props() { return this._props as IModalProps; } protected render(): string { - const {caption = '', okText = '', cancelText = ''} = this.props; + const {caption = '', okText = '', cancelText = '',children=''} = this.props; return (` -
+ ${chat.last_message?`
-

${chat.last_message.content}

+

${chat.last_message?.content}

{{{ Button type="number" caption=${chat.unread_count}}}} -
+
`:`
+

no messages

+
`} + `) } diff --git a/src/components/chat-list/chat-list.pcss b/src/components/chat-list/chat-list.pcss index 84e7743a8..674220f07 100644 --- a/src/components/chat-list/chat-list.pcss +++ b/src/components/chat-list/chat-list.pcss @@ -8,7 +8,9 @@ .chat-list__header { display: flex; - justify-content: flex-end; + justify-content: space-between; + align-items: center; + margin:10px 0; } .chat-list__search { @@ -22,4 +24,3 @@ list-style-type: none; } } - diff --git a/src/components/chat-list/chat-list.ts b/src/components/chat-list/chat-list.ts index 23a34af67..504fcd724 100644 --- a/src/components/chat-list/chat-list.ts +++ b/src/components/chat-list/chat-list.ts @@ -1,33 +1,60 @@ -import {IProps,Block} from "../../utils/Block"; +import {IProps, Block} from "../../utils/Block"; import {IChat} from "../../models/IChat.ts"; import {ChatItem} from "../index.ts"; import {IChatItemProps} from "../chat-item/chat-item.ts"; +import {IUser} from "../../models/IUser.ts"; +import modalController from "../../utils/modal-controller.ts"; +import ModalPrompt from "../modal-prompt"; +import {createChat} from "../../services/chat.ts"; +import {initChatPage} from "../../services/app.ts"; interface IChatListProps extends IProps { - list:IChat[] + list: IChat[], + currentUser: IUser | null, + showModalAddChat:()=>void } export class ChatList extends Block { constructor(props: IChatListProps) { + props.currentUser = window.user; + props.list=window.chats||[]; + props.showModalAddChat = ()=>{ + modalController.addModal((new ModalPrompt({ + caption: 'Add Chat', + labelText: 'Title Chat', + okText: 'Add Chat', + ref:"modal", + okClick: (result:string) => { + console.log(result); + createChat(result).then(()=>initChatPage()) + }, + })) as unknown as Block); + modalController.openModal(); + } + super(props); + console.log(props.list) } - getChats(list:IChat[]):string{ - if(!list||list.length===0)return ''; - return list.map(chat=>{ - const chatBlock=new ChatItem({chat:chat} as IChatItemProps) - return(` + getChats(list: IChat[]): string { + if (!list || list.length === 0) return ''; + return list.map(chat => { + const chatBlock = new ChatItem({chat: chat} as IChatItemProps) + return (` ${chatBlock.renderForList()} `) }).join('') } + protected render(): string { - const {list } = this._props as IChatListProps; + const {list,currentUser} = this._props as IChatListProps; return (`