From 295a8e0eef93e013ab9be98899fa979ce96e5408 Mon Sep 17 00:00:00 2001 From: Sergey Melyukov Date: Fri, 17 Mar 2023 11:25:54 +0300 Subject: [PATCH 1/2] week-4 --- .../loft-photo-lite-3/images/arrow-left.svg | 4 + projects/loft-photo-lite-3/images/button.svg | 17 + projects/loft-photo-lite-3/images/chat.svg | 3 + projects/loft-photo-lite-3/images/exit.svg | 5 + .../loft-photo-lite-3/images/heart-red.svg | 3 + projects/loft-photo-lite-3/images/heart.svg | 3 + projects/loft-photo-lite-3/images/logo.svg | 11 + projects/loft-photo-lite-3/images/send.svg | 3 + projects/loft-photo-lite-3/images/vert1.svg | 22 ++ projects/loft-photo-lite-3/images/vert2.svg | 22 ++ projects/loft-photo-lite-3/images/vert3.svg | 22 ++ projects/loft-photo-lite-3/index.js | 8 + projects/loft-photo-lite-3/layout.html | 64 ++++ projects/loft-photo-lite-3/loginPage.js | 15 + projects/loft-photo-lite-3/mainPage.js | 12 + projects/loft-photo-lite-3/model.js | 24 ++ projects/loft-photo-lite-3/pages.js | 9 + projects/loft-photo-lite-3/readme.md | 41 +++ projects/loft-photo-lite-3/styles.css | 348 ++++++++++++++++++ 19 files changed, 636 insertions(+) create mode 100644 projects/loft-photo-lite-3/images/arrow-left.svg create mode 100644 projects/loft-photo-lite-3/images/button.svg create mode 100644 projects/loft-photo-lite-3/images/chat.svg create mode 100644 projects/loft-photo-lite-3/images/exit.svg create mode 100644 projects/loft-photo-lite-3/images/heart-red.svg create mode 100644 projects/loft-photo-lite-3/images/heart.svg create mode 100644 projects/loft-photo-lite-3/images/logo.svg create mode 100644 projects/loft-photo-lite-3/images/send.svg create mode 100644 projects/loft-photo-lite-3/images/vert1.svg create mode 100644 projects/loft-photo-lite-3/images/vert2.svg create mode 100644 projects/loft-photo-lite-3/images/vert3.svg create mode 100644 projects/loft-photo-lite-3/index.js create mode 100644 projects/loft-photo-lite-3/layout.html create mode 100644 projects/loft-photo-lite-3/loginPage.js create mode 100644 projects/loft-photo-lite-3/mainPage.js create mode 100644 projects/loft-photo-lite-3/model.js create mode 100644 projects/loft-photo-lite-3/pages.js create mode 100644 projects/loft-photo-lite-3/readme.md create mode 100644 projects/loft-photo-lite-3/styles.css diff --git a/projects/loft-photo-lite-3/images/arrow-left.svg b/projects/loft-photo-lite-3/images/arrow-left.svg new file mode 100644 index 000000000..a4e4c339a --- /dev/null +++ b/projects/loft-photo-lite-3/images/arrow-left.svg @@ -0,0 +1,4 @@ + + + + diff --git a/projects/loft-photo-lite-3/images/button.svg b/projects/loft-photo-lite-3/images/button.svg new file mode 100644 index 000000000..6ce85ea9f --- /dev/null +++ b/projects/loft-photo-lite-3/images/button.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/projects/loft-photo-lite-3/images/chat.svg b/projects/loft-photo-lite-3/images/chat.svg new file mode 100644 index 000000000..fc47d01e1 --- /dev/null +++ b/projects/loft-photo-lite-3/images/chat.svg @@ -0,0 +1,3 @@ + + + diff --git a/projects/loft-photo-lite-3/images/exit.svg b/projects/loft-photo-lite-3/images/exit.svg new file mode 100644 index 000000000..d28c122e1 --- /dev/null +++ b/projects/loft-photo-lite-3/images/exit.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/projects/loft-photo-lite-3/images/heart-red.svg b/projects/loft-photo-lite-3/images/heart-red.svg new file mode 100644 index 000000000..e9985dca6 --- /dev/null +++ b/projects/loft-photo-lite-3/images/heart-red.svg @@ -0,0 +1,3 @@ + + + diff --git a/projects/loft-photo-lite-3/images/heart.svg b/projects/loft-photo-lite-3/images/heart.svg new file mode 100644 index 000000000..4bcdacd80 --- /dev/null +++ b/projects/loft-photo-lite-3/images/heart.svg @@ -0,0 +1,3 @@ + + + diff --git a/projects/loft-photo-lite-3/images/logo.svg b/projects/loft-photo-lite-3/images/logo.svg new file mode 100644 index 000000000..12685673d --- /dev/null +++ b/projects/loft-photo-lite-3/images/logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/projects/loft-photo-lite-3/images/send.svg b/projects/loft-photo-lite-3/images/send.svg new file mode 100644 index 000000000..5a55b025c --- /dev/null +++ b/projects/loft-photo-lite-3/images/send.svg @@ -0,0 +1,3 @@ + + + diff --git a/projects/loft-photo-lite-3/images/vert1.svg b/projects/loft-photo-lite-3/images/vert1.svg new file mode 100644 index 000000000..d5d86e658 --- /dev/null +++ b/projects/loft-photo-lite-3/images/vert1.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/loft-photo-lite-3/images/vert2.svg b/projects/loft-photo-lite-3/images/vert2.svg new file mode 100644 index 000000000..0f5e75ed2 --- /dev/null +++ b/projects/loft-photo-lite-3/images/vert2.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/loft-photo-lite-3/images/vert3.svg b/projects/loft-photo-lite-3/images/vert3.svg new file mode 100644 index 000000000..7b481af03 --- /dev/null +++ b/projects/loft-photo-lite-3/images/vert3.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/loft-photo-lite-3/index.js b/projects/loft-photo-lite-3/index.js new file mode 100644 index 000000000..ba96b1476 --- /dev/null +++ b/projects/loft-photo-lite-3/index.js @@ -0,0 +1,8 @@ +import pages from './pages'; +import mainPage from './mainPage'; +import loginPage from './loginPage'; +import('./styles.css'); + +pages.openPage('login'); +loginPage.handleEvents(); +mainPage.handleEvents(); diff --git a/projects/loft-photo-lite-3/layout.html b/projects/loft-photo-lite-3/layout.html new file mode 100644 index 000000000..89845cfed --- /dev/null +++ b/projects/loft-photo-lite-3/layout.html @@ -0,0 +1,64 @@ + + + + + + Loft Photo + + + +
+ + + +
+ + diff --git a/projects/loft-photo-lite-3/loginPage.js b/projects/loft-photo-lite-3/loginPage.js new file mode 100644 index 000000000..87cf95640 --- /dev/null +++ b/projects/loft-photo-lite-3/loginPage.js @@ -0,0 +1,15 @@ +import model from './model'; +import pages from './pages'; +import mainPage from './mainPage'; + +export default { + handleEvents() { + document.querySelector('.page-login-button').addEventListener('click', async () => { + await model.login(); + await model.init(); + + pages.openPage('main'); + await mainPage.getNextPhoto(); + }); + }, +}; diff --git a/projects/loft-photo-lite-3/mainPage.js b/projects/loft-photo-lite-3/mainPage.js new file mode 100644 index 000000000..925f5137c --- /dev/null +++ b/projects/loft-photo-lite-3/mainPage.js @@ -0,0 +1,12 @@ +import model from './model'; + +export default { + async getNextPhoto() { + const { friend, id, url } = await model.getNextPhoto(); + this.setFriendAndPhoto(friend, id, url); + }, + + setFriendAndPhoto(friend, id, url) {}, + + handleEvents() {}, +}; diff --git a/projects/loft-photo-lite-3/model.js b/projects/loft-photo-lite-3/model.js new file mode 100644 index 000000000..6c2e381bb --- /dev/null +++ b/projects/loft-photo-lite-3/model.js @@ -0,0 +1,24 @@ +export default { + getRandomElement(array) {}, + + async getNextPhoto() {}, + + login() {}, + + init() {}, + + photoCache: {}, + getFriendPhotos(id) { + const photos = this.photoCache[id]; + + if (photos) { + return photos; + } + + // const photos = вместо этого комментария вставьте код для получения фотографии друга из ВК + + this.photoCache[id] = photos; + + return photos; + }, +}; diff --git a/projects/loft-photo-lite-3/pages.js b/projects/loft-photo-lite-3/pages.js new file mode 100644 index 000000000..7ed7899ab --- /dev/null +++ b/projects/loft-photo-lite-3/pages.js @@ -0,0 +1,9 @@ +const pagesMap = { + login: '.page-login', + main: '.page-main', + profile: '.page-profile', +}; + +export default { + openPage(name) {}, +}; diff --git a/projects/loft-photo-lite-3/readme.md b/projects/loft-photo-lite-3/readme.md new file mode 100644 index 000000000..f1dfa1834 --- /dev/null +++ b/projects/loft-photo-lite-3/readme.md @@ -0,0 +1,41 @@ +## Страница входа и листалка фотографий + +### Часть 1 + +Возьмите за основу свой вариант `model.js` и `pages.js`, которые вы делали на предыдущих неделях. + +В [model.js](model.js) реализуйте: +- возможность логиниться в ВК (в методе `login`) +- возможность получать список друзей (в методе `init`) +- возможность получать список фотографий друга (в методе `getFriendPhotos`) + +С этого момента, метод getNextPhoto модели должен опираться не на файлы `friends.json` и `photos.json`, а на данные из ВК + +> В методе init получите список друзей и сохраните их в какой-нибудь внешней переменной или в свойстве модели +> +> В методе getFriendPhotos уже реализована базовая логика кеширования фотографий, чтобы каждый раз заново не загружать фото друга, для которого вы уже получали список фото. Вам необходимо только написать код для получения самих фото из ВК + +Имейте в виду, что у каждой фотографии в ВК есть свойство `sizes`, которое содержит список одних и тех же изображений, но разного размера. +Вам необходимы изображения с шириной не менее `360px`. +Попробуйте вывести значения свойства `sizes` в консоль или воспользуйтесь отладчиком, чтобы посмотреть содержимое свойства. + +Страница входа находится в [loginPage.js](loginPage.js) и уже умеет вызывать нужные методы модели при нажатии на кнопку `Войти`. + +Страница входа открывается автоматически (см. файл [index.js](index.js)). + +### Часть 2 + +В файле [mainPage.js](mainPage.js) содержится код главной страницы. + +Здесь вам необходимо реализовать листалку фотографий. +Главная страницы уже содержит реализованный метод `getNextPhoto`, который вызывает метод `getNextPhoto` из модели и затем передает результат в метод `setFriendAndPhoto`. +Его-то вам и нужно реализовать самостоятельно. +Смысл метода `setFriendAndPhoto` в том, чтобы получить информацию о фото (`id` и `url`) и владельце фото и установить эту информацию в соответствующие элементы на странице. + +Далее реализуйте листалку фото. +Листалка можно работать либо вверх-вниз, либо влево-вправо (выберите что вам большое нравится). +Вы можете обработать как события `mousedown`/`mouseup`, так и события `touchstart`/`touchend`. + +> События touchstart/touchend будут работать только на touch-устройствах или в режиме эмуляции. Для включения режима эмуляции воспользуйтесь панелью DevTool в браузере. +> +> Все обработчики событий пишите в методе handleEvents diff --git a/projects/loft-photo-lite-3/styles.css b/projects/loft-photo-lite-3/styles.css new file mode 100644 index 000000000..62d5fba21 --- /dev/null +++ b/projects/loft-photo-lite-3/styles.css @@ -0,0 +1,348 @@ +/* base */ + +body { + font-family: "Roboto Light", Geneva, Arial, Helvetica, sans-serif; +} + +.hidden { + display: none !important; +} + +a { + text-decoration: none; +} + +/* app */ + +#app { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + display: flex; + + align-items: center; + justify-content: center; +} + +.page { + height: 100%; + width: 360px; + position: relative; +} + +/* page login */ + +.page-login { + display: flex; + justify-content: center; + background: #1C1B1F; +} + +.page-login-button { + border: none; + background: url('images/button.svg'); + width: 219px; + height: 40px; + position: absolute; + bottom: 60px; + margin: 0 auto; +} + +.page-login-logo { + top: 429px; + position: absolute; + gap: 16px; + display: flex; + flex-direction: column; + align-items: center; +} + +.page-login-image { + width: 147px; + height: 24px; + background: url('images/logo.svg'); +} + +.page-login-text { + font-size: 14px; + line-height: 20px; + text-align: center; + width: 237px; + color: #B0B0B0; +} + +.page-login-vert1, .page-login-vert2, .page-login-vert3 { + width: 71px; + height: 333px; + position: absolute; +} + +.page-login-vert1 { + top: 59px; + left: 49px; + background: linear-gradient(180deg, rgba(28, 27, 31, 0) 80%, #1C1B1F 100%), url('images/vert1.svg'); +} + +.page-login-vert2 { + top: 81px; + left: 144px; + background: linear-gradient(180deg, rgba(28, 27, 31, 0) 80%, #1C1B1F 100%), url('images/vert2.svg'); +} + +.page-login-vert3 { + top: 59px; + left: 239px; + background: linear-gradient(180deg, rgba(28, 27, 31, 0) 80%, #1C1B1F 100%), url('images/vert3.svg'); +} + +/* page main */ + +.page-main .component-header { + position: absolute; + display: flex; + height: 80px; + top: 0; + left: 0; + right: 0; + background: rgba(0 0 0 / 25%); + padding: 0 24px; +} + +.page-main .component-header-profile-link { + display: flex; + align-items: center; +} + +.page-main .component-header-photo { + width: 40px; + height: 40px; + border-radius: 50%; + flex-shrink: 0; +} + +.page-main .component-header-name { + margin-left: 8px; + font-weight: 400; + font-size: 16px; + color: white; +} + +.page-main .component-footer { + position: absolute; + display: flex; + height: 80px; + bottom: 0; + left: 0; + right: 0; + background: rgba(0 0 0 / 25%); + padding: 0 24px; +} + +.page-main .component-footer-container { + display: flex; + align-items: center; + width: 100%; +} + +.page-main .component-footer-container-profile-link { + margin-left: auto; +} + +.page-main .component-footer-photo { + width: 40px; + height: 40px; + border-radius: 50%; +} + +.page-main .component-footer-container-social-comments, +.page-main .component-footer-container-social-likes { + color: white; + display: flex; + align-items: center; +} + +.page-main .component-footer-container-social-comments:before, +.page-main .component-footer-container-social-likes:before { + display: inline-block; + content: ''; + width: 20px; + height: 20px; + margin-right: 6px; +} + +.page-main .component-footer-container-social-comments:before { + background: url("images/chat.svg"); +} + +.page-main .component-footer-container-social-likes:before { + background: url("images/heart.svg"); + margin-left: 18px; +} + +.page-main .component-footer-container-social-likes.liked:before { + background: url("images/heart-red.svg"); + margin-left: 18px; +} + +.page-main .component-photo { + height: 100%; + width: 360px; + position: relative; + + background-size: cover; + background-position: center; +} + +.component-comments { + position: fixed; + bottom: 0; + left: 0; + right: 0; + top: 0; + background: rgba(0, 0, 0, 0.4); +} + +.component-comments-container { + position: absolute; + display: flex; + flex-direction: column; + top: 50vh; + bottom: 0; + left: 0; + right: 0; + padding: 16px; + border-radius: 28px 28px 0 0; + background: white; +} + +.component-comments-container-title { + font-size: 14px; + text-align: center; + width: 100%; +} + +.component-comments-container-list { + margin-top: 24px; + flex-grow: 1; + display: flex; + gap: 12px; + flex-direction: column; + overflow-y: auto; + margin-bottom: 14px +} + +.component-comments-container-form { + display: flex; + align-items: center; + gap: 16px; + height: 48px; +} + +.component-comments-container-form-input { + box-sizing: border-box; + border: 1px solid #E0E0E0; + border-radius: 32px; + flex-grow: 1; + height: 48px; +} + +.component-comments-container-form-input, +.component-comments-container-form-input, +.component-comments-container-form-input, +.component-comments-container-form-input { + padding: 14px 16px; +} + +.component-comments-container-form-send { + background: url('images/send.svg'); + width: 40px; + height: 40px; +} + +.component-comment { + display: flex; + gap: 8px +} + +.component-comment-photo { + width: 24px; + height: 24px; + border-radius: 50%; + background-position: center; + background-size: cover; +} + +.component-comment-content { + flex-direction: column; +} + +.component-comment-name { + font-size: 12px; +} + +.component-comment-text { + font-size: 14px; +} + +/* page profile */ + +.page-profile { + margin-top: 52px; +} + +.page-profile-back { + background: url('images/arrow-left.svg'); + width: 24px; + height: 24px; + + position: absolute; + left: 24px; +} + +.page-profile-exit { + background: url('images/exit.svg'); + width: 24px; + height: 24px; + + position: absolute; + right: 24px; +} + +.component-user-photos { + display: flex; + flex-wrap: wrap; + gap: 8px; + padding: 24px 16px 16px 16px; +} + +.component-user-photo { + width: 104px; + height: 104px; + background-size: cover; + background-repeat: no-repeat; + background-position: center; +} + +.page-profile .component-user-info { + display: flex; + flex-direction: column; + align-items: center; +} + +.page-profile .component-user-info-photo { + height: 72px; + width: 72px; + border-radius: 50%; + + background-size: cover; + background-position: center; +} + +.page-profile .component-user-info-name { + font-weight: 400; + font-size: 18px; + line-height: 26px; + margin-top: 8px; +} \ No newline at end of file From b74ea69ce0bc00eceef6ef0a32190a13cd3a4b64 Mon Sep 17 00:00:00 2001 From: Vadim Shvetsov Date: Tue, 12 Mar 2024 23:25:37 +1000 Subject: [PATCH 2/2] =?UTF-8?q?=D0=B3=D0=BE=D1=82=D0=BE=D0=B2=D0=BE=20?= =?UTF-8?q?=D0=B4=D0=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- projects/loft-photo-lite-3/mainPage.js | 27 ++++++++- projects/loft-photo-lite-3/model.js | 79 +++++++++++++++++++++++--- 2 files changed, 97 insertions(+), 9 deletions(-) diff --git a/projects/loft-photo-lite-3/mainPage.js b/projects/loft-photo-lite-3/mainPage.js index 925f5137c..ba2710ced 100644 --- a/projects/loft-photo-lite-3/mainPage.js +++ b/projects/loft-photo-lite-3/mainPage.js @@ -1,3 +1,4 @@ +import { doc } from 'prettier'; import model from './model'; export default { @@ -6,7 +7,29 @@ export default { this.setFriendAndPhoto(friend, id, url); }, - setFriendAndPhoto(friend, id, url) {}, + setFriendAndPhoto(friend, id, url) { + const photoComp = document.querySelector('.component-photo'); + const headerPhotoComp = document.querySelector('.component-header-photo'); + const photoNameComp = document.querySelector('.component-header-name'); - handleEvents() {}, + photoComp.style.backgrounImage = `url(${url})`; + headerPhotoComp.style.backgrounImage = `url('${frind.photo_50}')`; + photoNameComp.innerText = `${fiend.first_name ?? ''} ${frind.last_name ?? ''}`; + }, + + handleEvents() { + let startFrom; + + document.querySelector('.component-photo').addEventListener('touchstart', (e) => { + e.preventDefault(); + startFrom = {y: e.changedTouches[0].pageY}; + }); + + document.querySelector('.component-photo').addEventListener('touchend', async (e) => { + const direction = e.changedTouches[0].pageY - startFrom.y; + if (direction < 0) { + await this.getNextPhoto(); + } + }); + }, }; diff --git a/projects/loft-photo-lite-3/model.js b/projects/loft-photo-lite-3/model.js index 6c2e381bb..c6f3b2354 100644 --- a/projects/loft-photo-lite-3/model.js +++ b/projects/loft-photo-lite-3/model.js @@ -1,21 +1,86 @@ export default { getRandomElement(array) {}, - async getNextPhoto() {}, + async getNextPhoto() { + const friend = this.getRandomElement(this.friends.item); + const photos = await this.getFriendPhotos(friend.id); + const photo = this.getRandomElement(photos.items); + const size = this.findSize(photo); - login() {}, + return {fiend, id: photo.id, url: size.url}; + }, - init() {}, + findSize(photo){ + const size = photo.sizes.find ((size) =>{size.width >= 360}) + if (!size) { + return photo.sizes.reduce ((biggest, current) => { + if (current.width > biggest.width) { + return current; + } + return biggest; + }, photo.sizes[0]); + } + }, + + async init (){ + this.photoCache = {}; + this.friends = await this.getFriends(); + }, + + login() { + return new Promise((resolve, reject) => { + VK.init ({ + apiId: APP_ID, + }) + VK.Auth.login((response) => { + if(response.session) { + resolve(response) + } else { + console.error (response); + reject(response); + } + }); + }); + }, + + logout () { + + }, + + callApi (method, params) { + params.v = params.v || '5.120'; + + return new Promise ((resolve, reject) => { + VK.api (method, params, (response) => { + if (response.error) {reject(new Error(response.error.error_msg));} else {resolve(response.response);} + }); + }); + + }, + + getPhotos(owner) { + const params = { + owner_if: owner, + }; + + return this.callApi ('photos.getAll', params); + }, + + getFriends () { + const params = { + friends: ['photo_50', 'photo_100'], + } + return this.callApi('friends.get', params); + }, - photoCache: {}, - getFriendPhotos(id) { - const photos = this.photoCache[id]; + async getFriendPhotos(id) { + let photos = this.photoCache[id]; if (photos) { return photos; } - // const photos = вместо этого комментария вставьте код для получения фотографии друга из ВК + photos = await this.getPhotos (id); this.photoCache[id] = photos;