diff --git a/.circleci/config.yml b/.circleci/config.yml index 188f34c..ff35dbf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,7 +19,6 @@ jobs: - restore_cache: keys: - v1-dependencies-{{ checksum "package.json" }} - - v1-dependencies- - run: yarn install - save_cache: paths: @@ -37,7 +36,6 @@ jobs: - restore_cache: keys: - v1-dependencies-{{ checksum "package.json" }} - - v1-dependencies- - run: npm install diff --git a/config.xml b/config.xml index 9ea3dc2..2914817 100644 --- a/config.xml +++ b/config.xml @@ -91,6 +91,7 @@ + diff --git a/package-lock.json b/package-lock.json index a0b6342..0f9d604 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1103,6 +1103,12 @@ "integrity": "sha512-4GbNCDs98uHCT/OMv40qQC/OpoPbYn9XdXeTiFwHBBFO6eJhYEPUu2zDKirXSbHlvDV8oZ9l8EQ+HrEx/YS9DQ==", "dev": true }, + "@types/socket.io-client": { + "version": "1.4.32", + "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.32.tgz", + "integrity": "sha512-Vs55Kq8F+OWvy1RLA31rT+cAyemzgm0EWNeax6BWF8H7QiiOYMJIdcwSDdm5LVgfEkoepsWkS+40+WNb7BUMbg==", + "dev": true + }, "@webassemblyjs/ast": { "version": "1.7.10", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.10.tgz", @@ -1463,6 +1469,7 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, + "optional": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -2730,7 +2737,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "dev": true, + "optional": true }, "constants-browserify": { "version": "1.0.0", @@ -8726,7 +8734,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true + "dev": true, + "optional": true }, "depd": { "version": "1.1.2", @@ -10344,6 +10353,7 @@ "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -10356,6 +10366,7 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, + "optional": true, "requires": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -10393,7 +10404,8 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true + "dev": true, + "optional": true }, "get-stream": { "version": "3.0.0", @@ -10637,7 +10649,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true + "dev": true, + "optional": true }, "has-value": { "version": "1.0.0", @@ -11349,7 +11362,8 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true + "dev": true, + "optional": true }, "is-windows": { "version": "1.0.2", @@ -12006,6 +12020,7 @@ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", @@ -12018,7 +12033,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "dev": true, + "optional": true } } }, @@ -12346,7 +12362,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true + "dev": true, + "optional": true }, "map-visit": { "version": "1.0.0", @@ -12978,6 +12995,7 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, + "optional": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -14080,6 +14098,7 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, + "optional": true, "requires": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", @@ -14091,6 +14110,7 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", @@ -14101,7 +14121,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "dev": true, + "optional": true } } }, @@ -14110,6 +14131,7 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, + "optional": true, "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" @@ -14120,6 +14142,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, + "optional": true, "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" @@ -14130,6 +14153,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, + "optional": true, "requires": { "pinkie-promise": "^2.0.0" } @@ -15409,6 +15433,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, + "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -16763,6 +16788,7 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, + "optional": true, "requires": { "string-width": "^1.0.2 || 2" } diff --git a/package.json b/package.json index 7f6705b..c7ffaed 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "core-js": "^2.5.4", "hammerjs": "^2.0.8", "rxjs": "~6.3.3", + "socket.io": "^2.2.0", "zone.js": "~0.8.26" }, "devDependencies": { @@ -64,6 +65,7 @@ "@types/jasmine": "~2.8.8", "@types/jasminewd2": "~2.0.3", "@types/node": "~10.12.0", + "@types/socket.io-client": "^1.4.32", "codelyzer": "~4.5.0", "jasmine-core": "~2.99.1", "jasmine-spec-reporter": "~4.2.1", diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 25b063d..724f454 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -16,6 +16,7 @@ const routes: Routes = [ { path: 'comments/:id', loadChildren: './pages/comments/comments.module#CommentsPageModule', canActivate: [LoginGuard] }, { path: 'user/:id', loadChildren: './pages/user/user.module#UserPageModule' }, { path: 'news/:id', loadChildren: './pages/news/news.module#NewsPageModule' }, + { path: 'chat-id/:id', loadChildren: './pages/chat-id/chat-id.module#ChatIdPageModule', canActivate: [LoginGuard] }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], diff --git a/src/app/modal/report/report.module.ts b/src/app/modal/report/report.module.ts new file mode 100644 index 0000000..6e05c53 --- /dev/null +++ b/src/app/modal/report/report.module.ts @@ -0,0 +1,18 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; + +import { IonicModule } from '@ionic/angular'; + +import { ReportPage } from 'src/app/modal/report/report.page'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule, + ], + declarations: [ReportPage], + entryComponents: [ReportPage] +}) +export class ReportPageModule {} diff --git a/src/app/modal/report/report.page.html b/src/app/modal/report/report.page.html new file mode 100644 index 0000000..8bf6cd1 --- /dev/null +++ b/src/app/modal/report/report.page.html @@ -0,0 +1,34 @@ + + + + + + + + Создание репорта + + + Отправить + + + + + + + + Название + + + + + Описание проблемы + + + + + diff --git a/src/app/modal/report/report.page.scss b/src/app/modal/report/report.page.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/modal/report/report.page.spec.ts b/src/app/modal/report/report.page.spec.ts new file mode 100644 index 0000000..8c7d2f1 --- /dev/null +++ b/src/app/modal/report/report.page.spec.ts @@ -0,0 +1,27 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ReportPage } from './report.page'; + +describe('ReportPage', () => { + let component: ReportPage; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ReportPage ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ReportPage); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/modal/report/report.page.ts b/src/app/modal/report/report.page.ts new file mode 100644 index 0000000..e5b8561 --- /dev/null +++ b/src/app/modal/report/report.page.ts @@ -0,0 +1,65 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Storage } from '@ionic/storage'; +import { ToastController, ModalController } from '@ionic/angular'; +import { ReportService } from 'src/app/providers/report.service'; +import { Firebase } from '@ionic-native/firebase/ngx'; +import { Form } from '@angular/forms'; + +@Component({ + selector: 'app-report', + templateUrl: './report.page.html', + styleUrls: ['./report.page.scss'], +}) +export class ReportPage implements OnInit { + // привязка к посту, пользователю + @Input() public post_id?: string; + @Input() public user_id?: string; + + public name: string; + public body: string; + + public reportForm: Form; + + constructor( + private modalController: ModalController, + private storage: Storage, + private toast: ToastController, + private rep: ReportService, + private firebase: Firebase + ) { } + + ngOnInit() { + } + + public async close() { + await this.modalController.dismiss({ action: 'closed' }); + } + + public async report() { + const user_local = await this.storage.get('user_local'); + try { + await this.rep.send({ + name: this.name, + body: this.body, + post_id: this.post_id, + user_id: this.user_id, + authod_id: user_local.id || '' + }); + + (await this.toast.create({ + message: 'Спасибо за репорт!', + duration: 5000 + })).present(); + this.modalController.dismiss({ action: 'sended' }); + } catch (e) { + // логируем в консоль браузера + console.error(e); + // логируем в фаербейс + await this.firebase.logError(e); + (await this.toast.create({ + message: 'Ошибка при отправке, попробуйте чуть позже', + duration: 5000 + })).present(); + } + } +} diff --git a/src/app/pages/chat-create/chat-create.page.html b/src/app/pages/chat-create/chat-create.page.html new file mode 100644 index 0000000..7cfd734 --- /dev/null +++ b/src/app/pages/chat-create/chat-create.page.html @@ -0,0 +1,51 @@ + + + + + Сохранить + + + {{title}} + + + + + + + + + Загрузить + + + + Название чата + + + + + + + Пользователи + + + + Пригласить: + + + + + + + + + + + + {{ user.name }} + + + Удалить + + + + diff --git a/src/app/pages/chat-create/chat-create.page.scss b/src/app/pages/chat-create/chat-create.page.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/pages/chat-create/chat-create.page.spec.ts b/src/app/pages/chat-create/chat-create.page.spec.ts new file mode 100644 index 0000000..41da6de --- /dev/null +++ b/src/app/pages/chat-create/chat-create.page.spec.ts @@ -0,0 +1,27 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChatCreatePage } from './chat-create.page'; + +describe('ChatCreatePage', () => { + let component: ChatCreatePage; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ChatCreatePage ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ChatCreatePage); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/chat-create/chat-create.page.ts b/src/app/pages/chat-create/chat-create.page.ts new file mode 100644 index 0000000..9714b25 --- /dev/null +++ b/src/app/pages/chat-create/chat-create.page.ts @@ -0,0 +1,95 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { ModalController, ToastController } from '@ionic/angular'; +import { IChat, IUser } from 'src/app/providers/interfaces'; +import { ImagePicker } from '@ionic-native/image-picker/ngx'; +import { ChatService } from 'src/app/providers/chat.service'; +import { UserService } from 'src/app/providers/user.service'; + +@Component({ + selector: 'app-chat-create', + templateUrl: './chat-create.page.html', + styleUrls: ['./chat-create.page.scss'], +}) +export class ChatCreatePage implements OnInit { + @Input() public info?: IChat; + @Input() public title: string; + public name: string; + public picture = 'https://avatars.mds.yandex.net/get-pdb/1532603/ac56ac6f-b354-4c5b-bf06-533910e0fae8/s1200'; + + public users: IUser[] = []; + public selectedUsers: IUser[] = []; + public newinvite = ''; + + constructor( + private modalController: ModalController, + private imagePicker: ImagePicker, + private toast: ToastController, + private chat: ChatService, + private user: UserService + ) { } + + ngOnInit() { + if (this.info) { + this.name = this.info.name; + this.picture = this.info.picture; + + this.info.users.forEach(async (id: string) => { + this.users.push(await this.user.get(id)); + }); + } + } + + public async save() { + if (this.info) { + await this.chat.editChat(this.info.id, this.info.name, this.info.picture); + } else { + await this.chat.createChat(this.name, this.picture); + } + console.log(this); + + this.modalController.dismiss(); + } + + public async pickImage() { + if (!await this.imagePicker.hasReadPermission()) { + await this.imagePicker.requestReadPermission(); + + (await this.toast.create({ + message: 'Попробуйте снова', + duration: 2000 + })).present(); + return; + } + const result = await this.imagePicker.getPictures({ + maximumImagesCount: 1 + }); + + // this.picture = await this.user.updateAvatar(result[0]); + + (await this.toast.create({ + message: 'Ваша аватарка успешно загружена и скоро обновиться (наверно)', + duration: 5000 + })).present(); + } + + public async remove(id: string) { + await this.chat.actionsChat(this.info.id, id, 'ban'); + this.users = this.users.filter((v: any) => v.id !== id); + } + + public async invite() { + try { + const user = await this.user.getName(this.newinvite); + await this.chat.actionsChat(this.info.id, user.id, 'add'); + + this.users.push(user); + } catch (e) { + console.error(e); + + (await this.toast.create({ + message: 'Пользователь с таким ником не найден!', + duration: 5000 + })).present(); + } + } +} diff --git a/src/app/pages/chat-id/chat-id.module.ts b/src/app/pages/chat-id/chat-id.module.ts new file mode 100644 index 0000000..d26c59a --- /dev/null +++ b/src/app/pages/chat-id/chat-id.module.ts @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { Routes, RouterModule } from '@angular/router'; + +import { IonicModule } from '@ionic/angular'; + +import { ChatIdPage } from './chat-id.page'; + +const routes: Routes = [ + { + path: '', + component: ChatIdPage + } +]; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule, + RouterModule.forChild(routes) + ], + declarations: [ChatIdPage] +}) +export class ChatIdPageModule {} diff --git a/src/app/pages/chat-id/chat-id.page.html b/src/app/pages/chat-id/chat-id.page.html new file mode 100644 index 0000000..116df2f --- /dev/null +++ b/src/app/pages/chat-id/chat-id.page.html @@ -0,0 +1,57 @@ + + + + + + + + {{ chatinfo.name }} + + + + + + + + = 30"> + + + + + + + + + + {{ msg.user.name }} {{ msg.createdAt | date:'dd.MM, HH:mm'}} + + + + + {{ msg.body }} + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/pages/chat-id/chat-id.page.scss b/src/app/pages/chat-id/chat-id.page.scss new file mode 100644 index 0000000..64772b0 --- /dev/null +++ b/src/app/pages/chat-id/chat-id.page.scss @@ -0,0 +1,159 @@ +$userBackgroundColor: #387ef5; +$toUserBackgroundColor: #fff; + +ion-content .scroll-content { + background-color: #f5f5f5; +} + +ion-footer { + box-shadow: 0 0 4px rgba(0, 0, 0, 0.11); + background-color: #fff; + // height: 255px; +} + +.line-breaker { + white-space: pre-line; +} + +.input-wrap { + padding: 5px; + display: flex; + + ion-button { + --padding-end: 0px; + --padding-start: 0px; + } + + textarea { + flex: 1; + border: 0; + border-bottom: 1px $userBackgroundColor; + border-style: solid; + } +} + +.message-wrap { + padding: 0 10px; + .message { + position: relative; + padding: 7px 0; + + .user-img { + position: absolute; + border-radius: 45px; + width: 45px; + height: 45px; + box-shadow: 0 0 2px rgba(0, 0, 0, 0.36); + } + + .msg-detail { + width: 100%; + display: inline-block; + + p { + margin: 0; + } + + .msg-info { + p { + font-size: .8em; + color: #888; + } + } + + .msg-content { + position: relative; + margin-top: 5px; + border-radius: 5px; + padding: 8px; + border: 1px solid #ddd; + color: #fff; + width: auto; + max-width: 80%; + + span.triangle { + border-radius: 2px; + height: 8px; + width: 8px; + top: 12px; + display: block; + border-style: solid; + border-width: 1px; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); + position: absolute; + } + } + + .images { + img { + border-radius: 10px; + } + } + + } + } + + .message.left { + .msg-content { + background-color: $toUserBackgroundColor; + float: left; + } + + .msg-detail { + padding-left: 60px; + } + + .user-img { + left: 0; + } + + .msg-content { + color: #343434 !important; + + span.triangle { + background-color: $toUserBackgroundColor; + border-color: $toUserBackgroundColor; + border-top-width: 0; + border-right-width: 0; + left: -5px; + } + } + + } + + .message.right { + .msg-detail { + padding-right: 60px; + + .msg-info { + text-align: right; + } + } + + .user-img { + right: 0; + } + + ion-spinner { + position: absolute; + right: 10px; + top: 50px; + } + + .msg-content { + background-color: $userBackgroundColor; + float: right; + + span.triangle { + background-color: $userBackgroundColor; + border-color: $userBackgroundColor; + border-bottom-width: 0; + border-left-width: 0; + right: -5px; + } + } + + } + +} diff --git a/src/app/pages/chat-id/chat-id.page.spec.ts b/src/app/pages/chat-id/chat-id.page.spec.ts new file mode 100644 index 0000000..79f1736 --- /dev/null +++ b/src/app/pages/chat-id/chat-id.page.spec.ts @@ -0,0 +1,27 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChatIdPage } from './chat-id.page'; + +describe('ChatIdPage', () => { + let component: ChatIdPage; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ChatIdPage ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ChatIdPage); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/chat-id/chat-id.page.ts b/src/app/pages/chat-id/chat-id.page.ts new file mode 100644 index 0000000..db295e0 --- /dev/null +++ b/src/app/pages/chat-id/chat-id.page.ts @@ -0,0 +1,114 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { LoadingController, IonContent } from '@ionic/angular'; +import { IChat, IMessage, IUserSelf } from 'src/app/providers/interfaces'; +import { ChatService } from 'src/app/providers/chat.service'; +import { UserService } from 'src/app/providers/user.service'; + +@Component({ + selector: 'app-chat-id', + templateUrl: './chat-id.page.html', + styleUrls: ['./chat-id.page.scss'], +}) +export class ChatIdPage implements OnInit { + + private id: string; + public chatinfo: IChat; + public user: IUserSelf; + public messages: IMessage[] = []; + + @ViewChild(IonContent) contentArea: IonContent; + private page = 0; + private attachments: any; + + /** + * @description contain current message + */ + public editorMsg: string; + + private spiner: HTMLIonLoadingElement; + + constructor( + private cs: ChatService, + private route: ActivatedRoute, + private router: Router, + private us: UserService, + private loadingController: LoadingController, + ) { } + + async ngOnInit() { + this.id = this.route.snapshot.paramMap.get('id'); + this.spiner = await this.loadingController.create({ + message: 'Загрузка...', + duration: 5000 + }); + + await this.spiner.present(); + await this.load(); + this.scrollToBottom(); + + await this.cs.initSocket(); + this.cs.onMessage().subscribe((msg: IMessage) => { + this.messages.push(msg); + this.scrollToBottom(); + }); + } + + public goBack() { + this.router.navigateByUrl('/tabs/chats'); + } + + public async update(event: any) { + this.load() + .then(() => event.target.complete()) + .catch(() => event.target.cansel()); + } + + public async loadMore(event: any) { + event.target.complete(); + // this.load() + // .then(() => event.target.complete()) + // .then(() => this.scrollToBottom()) + // .catch(() => event.target.cansel()); + } + + public async msgmenu(event: any) { + console.log(this); + } + + public openUser(id: string): void { + this.router.navigateByUrl(`/user/${id}`); + } + + public async send() { + if (!this.editorMsg) { + return; + } + await this.cs.send(this.id, this.editorMsg, this.attachments); + this.editorMsg = ''; + } + + private async load() { + this.page += 1; + this.user = await this.us.getSelf(); + const messages = await this.cs.getMessages(this.id, this.page); + this.chatinfo = await this.cs.get(this.id); + + messages.forEach((msg: IMessage) => { + this.messages.unshift(msg); + }); + + await this.spiner.dismiss(); + } + + /** + * @description Позволяет спускаться вниз + */ + private scrollToBottom() { + setTimeout(() => { + if (this.contentArea.scrollToBottom) { + this.contentArea.scrollToBottom(); + } + }, 0); + } +} diff --git a/src/app/pages/chats/chats.module.ts b/src/app/pages/chats/chats.module.ts new file mode 100644 index 0000000..ab52530 --- /dev/null +++ b/src/app/pages/chats/chats.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { Routes, RouterModule } from '@angular/router'; + +import { IonicModule } from '@ionic/angular'; + +import { ChatsPage } from './chats.page'; +import { ChatCreatePage } from '../chat-create/chat-create.page'; + +const routes: Routes = [ + { + path: '', + component: ChatsPage + } +]; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule, + RouterModule.forChild(routes) + ], + declarations: [ChatsPage, ChatCreatePage], + entryComponents: [ChatCreatePage] +}) +export class ChatsPageModule {} diff --git a/src/app/pages/chats/chats.page.html b/src/app/pages/chats/chats.page.html new file mode 100644 index 0000000..11a4f61 --- /dev/null +++ b/src/app/pages/chats/chats.page.html @@ -0,0 +1,49 @@ + + + + + + + + Активные чаты + + + + + + + + + + + + + + + + + + + {{ item.name }} + + + + Изменить + Удалить + + + + + + + diff --git a/src/app/pages/chats/chats.page.scss b/src/app/pages/chats/chats.page.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/pages/chats/chats.page.spec.ts b/src/app/pages/chats/chats.page.spec.ts new file mode 100644 index 0000000..e535ace --- /dev/null +++ b/src/app/pages/chats/chats.page.spec.ts @@ -0,0 +1,27 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChatsPage } from './chats.page'; + +describe('ChatsPage', () => { + let component: ChatsPage; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ChatsPage ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ChatsPage); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/pages/chats/chats.page.ts b/src/app/pages/chats/chats.page.ts new file mode 100644 index 0000000..0cd93ff --- /dev/null +++ b/src/app/pages/chats/chats.page.ts @@ -0,0 +1,68 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { IChat, IUserSelf } from 'src/app/providers/interfaces'; +import { Router } from '@angular/router'; +import { ChatService } from 'src/app/providers/chat.service'; +import { ModalController } from '@ionic/angular'; +import { ChatCreatePage } from '../chat-create/chat-create.page'; +import { Storage } from '@ionic/storage'; + +@Component({ + selector: 'app-chats', + templateUrl: './chats.page.html', + styleUrls: ['./chats.page.scss'], +}) +export class ChatsPage implements OnInit { + + public chats: IChat[] = []; + @ViewChild('myList') private slidingList: any; + + private userlocal: IUserSelf; + + constructor( + private router: Router, + private cs: ChatService, + private modalController: ModalController, + private storage: Storage + ) { } + + async ngOnInit() { + await this.load(); + + this.userlocal = await this.storage.get('user_local'); + } + + public async createChat(chatinfo?: IChat) { + const modal = await this.modalController.create({ + component: ChatCreatePage, + componentProps: { + title: chatinfo ? 'Редактирование' : 'Создание нового чата', + info: chatinfo + } + }); + await modal.present(); + await modal.onDidDismiss(); + await this.load(); + } + + public async update(event: any) { + this.load() + .then(() => event.target.complete()) + .catch(() => event.target.cansel()); + } + + public async open(id: string) { + this.router.navigateByUrl(`/chat-id/${id}`); + } + + public async remove(id: string) { + await this.cs.deleteChat(id); + await this.load(); + } + + private async load(): Promise { + if (this.chats === []) { + await this.slidingList.closeOpened(); + } + this.chats = await this.cs.getAll(); + } +} diff --git a/src/app/pages/comments/comments.module.ts b/src/app/pages/comments/comments.module.ts index c7bf342..9e4c29c 100644 --- a/src/app/pages/comments/comments.module.ts +++ b/src/app/pages/comments/comments.module.ts @@ -6,6 +6,7 @@ import { Routes, RouterModule } from '@angular/router'; import { IonicModule } from '@ionic/angular'; import { CommentsPage } from './comments.page'; +import { ReportPageModule } from 'src/app/modal/report/report.module'; const routes: Routes = [ { @@ -19,7 +20,8 @@ const routes: Routes = [ CommonModule, FormsModule, IonicModule, - RouterModule.forChild(routes) + RouterModule.forChild(routes), + ReportPageModule ], declarations: [CommentsPage] }) diff --git a/src/app/pages/comments/comments.page.ts b/src/app/pages/comments/comments.page.ts index c85c5f5..5440cde 100644 --- a/src/app/pages/comments/comments.page.ts +++ b/src/app/pages/comments/comments.page.ts @@ -4,8 +4,9 @@ import { IComment, IPost } from 'src/app/providers/interfaces'; import { ActivatedRoute, Router } from '@angular/router'; import { Storage } from '@ionic/storage'; import { Firebase } from '@ionic-native/firebase/ngx'; -import { ToastController } from '@ionic/angular'; +import { ToastController, ModalController } from '@ionic/angular'; import { ReportService } from 'src/app/providers/report.service'; +import { ReportPage } from 'src/app/modal/report/report.page'; @Component({ selector: 'app-comments', @@ -31,6 +32,7 @@ export class CommentsPage implements OnInit { private storage: Storage, private firebase: Firebase, private toast: ToastController, + private modalController: ModalController ) { } ngOnInit() { @@ -95,28 +97,14 @@ export class CommentsPage implements OnInit { } public async report(item: IComment) { - try { - await this.rep.send({ - body: 'comment', - post_id: this.id, - user_id: item.user.id, - authod_id: this.local_user.id - }); - - (await this.toast.create({ - message: 'Спасибо за репорт!', - duration: 5000 - })).present(); - } catch (e) { - // логируем в консоль браузера - console.error(e); - // логируем в фаербейс - await this.firebase.logError(e); - (await this.toast.create({ - message: 'Ошибка при отправке, попробуйте чуть позже', - duration: 5000 - })).present(); - } + const modal = await this.modalController.create({ + component: ReportPage, + componentProps: { + post_id: this.id + } + }); + await modal.present(); + await modal.onDidDismiss(); } private async load() { diff --git a/src/app/pages/info/info.module.ts b/src/app/pages/info/info.module.ts index d51eda1..69eae74 100644 --- a/src/app/pages/info/info.module.ts +++ b/src/app/pages/info/info.module.ts @@ -6,6 +6,8 @@ import { Routes, RouterModule } from '@angular/router'; import { IonicModule } from '@ionic/angular'; import { InfoPage } from './info.page'; +import { ReportPage } from 'src/app/modal/report/report.page'; +import { ReportPageModule } from 'src/app/modal/report/report.module'; const routes: Routes = [ { @@ -19,7 +21,8 @@ const routes: Routes = [ CommonModule, FormsModule, IonicModule, - RouterModule.forChild(routes) + RouterModule.forChild(routes), + ReportPageModule ], declarations: [InfoPage] }) diff --git a/src/app/pages/info/info.page.ts b/src/app/pages/info/info.page.ts index 3000e12..9dc115b 100644 --- a/src/app/pages/info/info.page.ts +++ b/src/app/pages/info/info.page.ts @@ -1,12 +1,12 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { LoadingController, ModalController, PopoverController, ActionSheetController, ToastController } from '@ionic/angular'; +import { LoadingController, ModalController, ActionSheetController } from '@ionic/angular'; import { IPostFull } from 'src/app/providers/interfaces'; import { PostService } from 'src/app/providers/post.service'; import { Firebase } from '@ionic-native/firebase/ngx'; import { Storage } from '@ionic/storage'; import { UserService } from 'src/app/providers/user.service'; -import { ReportService } from 'src/app/providers/report.service'; +import { ReportPage } from 'src/app/modal/report/report.page'; @Component({ selector: 'app-info', @@ -29,8 +29,7 @@ export class InfoPage implements OnInit { private user: UserService, private storage: Storage, private asc: ActionSheetController, - private toast: ToastController, - private rep: ReportService, + private modalController: ModalController, private firebase: Firebase ) {} @@ -116,27 +115,13 @@ export class InfoPage implements OnInit { } public async report() { - const user_local = await this.storage.get('user_local'); - try { - await this.rep.send({ - body: 'manga', - post_id: this.id, - authod_id: user_local.id || '' - }); - - (await this.toast.create({ - message: 'Спасибо за репорт!', - duration: 5000 - })).present(); - } catch (e) { - // логируем в консоль браузера - console.error(e); - // логируем в фаербейс - await this.firebase.logError(e); - (await this.toast.create({ - message: 'Ошибка при отправке, попробуйте чуть позже', - duration: 5000 - })).present(); - } + const modal = await this.modalController.create({ + component: ReportPage, + componentProps: { + post_id: this.id + } + }); + await modal.present(); + await modal.onDidDismiss(); } } diff --git a/src/app/pages/reader/reader.page.ts b/src/app/pages/reader/reader.page.ts index 5c8391c..20dc9c2 100644 --- a/src/app/pages/reader/reader.page.ts +++ b/src/app/pages/reader/reader.page.ts @@ -60,6 +60,9 @@ export class ReaderPage implements OnInit { await this.storage.ready(); this.preload = (await this.storage.get('image_preload')); + if (this.preload === undefined) { + this.preload = true; + } this.info = await this.post.get(this.route.snapshot.paramMap.get('id')); diff --git a/src/app/pages/search-results/search-results.page.html b/src/app/pages/search-results/search-results.page.html index 9a5c68b..9f518c2 100644 --- a/src/app/pages/search-results/search-results.page.html +++ b/src/app/pages/search-results/search-results.page.html @@ -5,7 +5,8 @@ - + + + + + + + + + + + {{ item.name }} + + {{ item.annotation }} + + {{ item.genre.join(', ') }} + + + + + + + + diff --git a/src/app/pages/search-results/search-results.page.scss b/src/app/pages/search-results/search-results.page.scss index e69de29..5be46f2 100644 --- a/src/app/pages/search-results/search-results.page.scss +++ b/src/app/pages/search-results/search-results.page.scss @@ -0,0 +1,13 @@ +.scroller { + .post { + .cover { + width: 150px; + height: 200px; + padding-right: var(--padding-start); + + img { + border-radius: 5px; + } + } + } +} diff --git a/src/app/pages/search-results/search-results.page.ts b/src/app/pages/search-results/search-results.page.ts index cfabbba..dcf1206 100644 --- a/src/app/pages/search-results/search-results.page.ts +++ b/src/app/pages/search-results/search-results.page.ts @@ -1,6 +1,6 @@ // tslint:disable:max-line-length -import { Component, OnInit } from '@angular/core'; -import { ModalController } from '@ionic/angular'; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ModalController, IonVirtualScroll } from '@ionic/angular'; import { SearchParamsPage } from '../search-params/search-params.page'; import { Router } from '@angular/router'; @@ -15,14 +15,15 @@ import { Storage } from '@ionic/storage'; styleUrls: ['./search-results.page.scss'], }) export class SearchResultsPage implements OnInit { - // используется для переключения вида отображения данных - private mode: boolean; - - public result: IPost[] = []; private query = null; private page = 0; private activegenres: string[] = []; + // virtualScroll + public render: any[] = []; + + @ViewChild('scroller') private scroller: IonVirtualScroll; + constructor( private modalController: ModalController, private router: Router, @@ -47,11 +48,11 @@ export class SearchResultsPage implements OnInit { } this.activegenres = result.data.activegenres; - this.result = []; + this.render = []; this.page = 0; if (this.query !== '') { - this.result = []; + this.render = []; await this.load(this.query); } else { await this.load(null); @@ -94,14 +95,15 @@ export class SearchResultsPage implements OnInit { custom: (this.activegenres.length !== 0) ? `&genre=${this.activegenres.join(',')}` : '' }); if (temp.length === 0 && this.page === 1) { - this.result = []; + this.render = []; this.page = 0; return; } else { - temp.forEach(i => { - this.result.push(i); - }); + temp.forEach((i: IPost) => this.render.push(i)); } + + // this.render = this.result; + this.scroller.checkEnd(); } /** @@ -112,7 +114,7 @@ export class SearchResultsPage implements OnInit { this.query = event.target.value; this.page = 0; - this.result = []; + this.render = []; console.log(event.target.value); if (this.query !== '') { @@ -131,5 +133,6 @@ export class SearchResultsPage implements OnInit { public loadNewPage(event: any) { this.load(); event.target.complete(); + console.log(this); } } diff --git a/src/app/providers/api.service.ts b/src/app/providers/api.service.ts index 3af9531..916649b 100644 --- a/src/app/providers/api.service.ts +++ b/src/app/providers/api.service.ts @@ -33,6 +33,7 @@ export class API { * @type {string} */ private baseURL = 'https://api.anibe.ru'; + // private baseURL = 'http://127.0.0.1:8080'; /** * @private * @description тип содержимого ответа и запроса diff --git a/src/app/providers/chat.service.ts b/src/app/providers/chat.service.ts new file mode 100644 index 0000000..d26e727 --- /dev/null +++ b/src/app/providers/chat.service.ts @@ -0,0 +1,218 @@ +import { Injectable } from '@angular/core'; +import { API } from './api.service'; +import { IPostFull, RequestParam, IPost, IComment, IChat, IMessage } from './interfaces'; +import { Storage } from '@ionic/storage'; +import { ToastController } from '@ionic/angular'; +import * as socketIo from 'socket.io-client'; +import { Observable } from 'rxjs'; + +const SERVER_URL = 'https://api.anibe.ru'; +// const SERVER_URL = 'http://127.0.0.1:2300'; + +@Injectable({ + providedIn: 'root' +}) +export class ChatService { + /** + * Экземпляр класса для работы с апи через небольшую обертку + */ + private api: API; + private token: string; + private ready: Promise; + private socket: any; + + constructor( + private storage: Storage, + private toast: ToastController + ) { + this.api = new API({ }); + } + + public async initSocket(): Promise { + this.socket = socketIo(SERVER_URL, { + // path: '/ws/', + query: { + token: await this.storage.get('token') || '' + } + }); + } + public send( + chat_id: string, + body: string, + attachments: { + images: string[], + links: string[], + videos: string[], + sticker: string + } = { images: [], links: [], videos: [], sticker: ''} + ): void { + this.socket.emit('new_message', { + chat_id, + body, + attachments + }); + } + public onMessage(): Observable { + return new Observable((observer: { next: (arg0: IMessage) => void; }) => { + this.socket.on('new_message', (data: IMessage) => observer.next(data)); + }); + } + public onEvent(event: Event): Observable { + return new Observable((observer: { next: () => void; }) => { + this.socket.on(event, () => observer.next()); + }); + } + + /** + * Получить чат по его айди + * @async + * @param {string} id uuid чата + * @returns {Promise} результат + */ + public async get(id: string): Promise { + this.token = await this.storage.get('token') || ''; + const url = `/chats/${id}`; + + const res = await this.api.get(url, { + 'Authorization': 'Bearer ' + this.token + }); + return JSON.parse(res.data); + } + + /** + * Получение всех активных чатов для пользователя + * @async + * @param {string} query Название чата, может быть null + * @returns {Promise} + */ + public async getAll(query?: string): Promise { + this.token = await this.storage.get('token') || ''; + let url = `/chats`; + if (query) { + url += `?q=${query}`; + } + + const res = await this.api.get(url, { + 'Authorization': 'Bearer ' + this.token + }); + + return JSON.parse(res.data).rows; + } + + /** + * Отправляет сообщение в чат + * @param chat_id uuid чата + * @param body сообщение + * @param attachments прикрепления к сообщению + */ + public async createMessage( + chat_id: string, + body: string, + attachments: { + images: string[], + links: string[], + videos: string[], + sticker: string + } = { images: [], links: [], videos: [], sticker: ''} + ): Promise { + this.token = await this.storage.get('token') || ''; + + const res = await this.api.post(`/messages/${chat_id}`, { + body, + attachments + }, { + 'Authorization': 'Bearer ' + this.token + }); + return JSON.parse(res.data); + } + + /** + * Получение сообщений с паджинацией + * @param {string} id uuid чата + */ + public async getMessages(id: string, page: number = 1): Promise { + this.token = await this.storage.get('token') || ''; + + const res = await this.api.get(`/messages/${id}?page=${page}`, { + 'Authorization': 'Bearer ' + this.token + }); + + return JSON.parse(res.data).rows; + } + + /** + * Отправляет сообщение в чат + * @param {string} name имя нового чата + * @param {string} picture картинка нового чата + */ + public async createChat( + name: string, + picture: string + ): Promise { + this.token = await this.storage.get('token') || ''; + + const res = await this.api.post(`/chats`, { + name, + picture + }, { + 'Authorization': 'Bearer ' + this.token + }); + return JSON.parse(res.data); + } + + /** + * Удаляет чат по его айди (только если админ или создатель) + * @param {string} chat_id id чата + */ + public async deleteChat(chat_id: string) { + this.token = await this.storage.get('token') || ''; + + await this.api.delete(`/chats/${chat_id}`, { + 'Authorization': 'Bearer ' + this.token + }); + return true; + } + + /** + * ВЫполняет действия внутри чата, например добавить нового пользователя + * @param chat_id id чата + * @param action действие (add, ban) + */ + public async actionsChat( + chat_id: string, + user_id: string, + action: string, + ): Promise { + this.token = await this.storage.get('token') || ''; + + await this.api.post(`/chats/${chat_id}/actions`, { + action, + user_id + }, { + 'Authorization': 'Bearer ' + this.token + }); + return true; + } + + /** + * Изменяет картинку и название чата + * @param chat_id id чата + * @param name название + * @param picture картинка + */ + public async editChat( + chat_id: string, + name: string, + picture: string + ): Promise { + this.token = await this.storage.get('token') || ''; + + const res = await this.api.put(`/chats/${chat_id}`, { + name, + picture + }, { + 'Authorization': 'Bearer ' + this.token + }); + return JSON.parse(res.data); + } +} diff --git a/src/app/providers/interfaces.ts b/src/app/providers/interfaces.ts index e6fb675..f184d25 100644 --- a/src/app/providers/interfaces.ts +++ b/src/app/providers/interfaces.ts @@ -95,3 +95,64 @@ export interface INotif { createdAt: string; updatedAt: string; } + + + +export interface IChat { + id: string; + name: string; + picture: string; + users: string[]; + admin: string; + lastmessage?: IMessage; + + createdAt: string; + updatedAt: string; +} + +export interface IMessage { + id: string; + body: string; + user: IUser; + attachments?: { + images?: string[]; + links?: string[]; + sticker?: string; + }; + + createdAt: string; + updatedAt: string; +} + +export interface IUserBadge { + name: string; + icon: string; +} + +export interface IUser { + id: string; + online: string; + name: string; + picture: string; + role: string; + badges: IUserBadge[]; +} + +export interface IUserSelf { + id: string; + online: string; + name: string; + picture: string; + desc: string; + email: string; + enablefcm: boolean; + role: string; + favorite: IPost[]; + thrown: IPost[]; + inprogress: IPost[]; + readed: IPost[]; + willread: IPost[]; + createdAt: string; + updatedAt: string; + badges: IUserBadge[]; +} diff --git a/src/app/providers/user.service.ts b/src/app/providers/user.service.ts index 64e572d..9a656a0 100644 --- a/src/app/providers/user.service.ts +++ b/src/app/providers/user.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { API } from './api.service'; import { Storage } from '@ionic/storage'; -import { INotif, IPost } from './interfaces'; +import { INotif, IPost, IUser } from './interfaces'; @Injectable({ providedIn: 'root' @@ -77,7 +77,7 @@ export class UserService { * @description получить информацию о пользователе * @param id uuid пользователя */ - public async get(id: string) { + public async get(id: string): Promise { await this.setToken(); const url = `/users/${id}`; @@ -87,6 +87,20 @@ export class UserService { return JSON.parse(res.data); } + /** + * @description получить информацию о пользователе по его нику + * @param name + */ + public async getName(name: string): Promise { + await this.setToken(); + const url = `/users/name/${name}`; + + const res = await this.api.get(url, { + 'Authorization': 'Bearer ' + this.token + }); + return JSON.parse(res.data); + } + /** * @description обновить информацию о пользователе * @param body новая информация о пользователе diff --git a/src/app/tabs/tabs.page.html b/src/app/tabs/tabs.page.html index 489e882..a174fcc 100644 --- a/src/app/tabs/tabs.page.html +++ b/src/app/tabs/tabs.page.html @@ -12,6 +12,11 @@ Поиск + + + Чаты + + Уведомления diff --git a/src/app/tabs/tabs.router.module.ts b/src/app/tabs/tabs.router.module.ts index 4c0d2ba..8a283b3 100644 --- a/src/app/tabs/tabs.router.module.ts +++ b/src/app/tabs/tabs.router.module.ts @@ -58,6 +58,17 @@ const routes: Routes = [ path: '', redirectTo: '/tabs/tab1', pathMatch: 'full' + }, + { + path: 'chats', + children: [ + { + path: '', + loadChildren: '../pages/chats/chats.module#ChatsPageModule' + // component: ChatsPage + } + ], + canActivate: [LoginGuard] } ] }, diff --git a/yarn.lock b/yarn.lock index f96f4ff..fdfe85c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -483,6 +483,11 @@ resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.14.tgz#0b20a2370e6b1b8322c9c3dfcaa409e6c7c0c0a9" integrity sha512-4GbNCDs98uHCT/OMv40qQC/OpoPbYn9XdXeTiFwHBBFO6eJhYEPUu2zDKirXSbHlvDV8oZ9l8EQ+HrEx/YS9DQ== +"@types/socket.io-client@^1.4.32": + version "1.4.32" + resolved "https://registry.yarnpkg.com/@types/socket.io-client/-/socket.io-client-1.4.32.tgz#988a65a0386c274b1c22a55377fab6a30789ac14" + integrity sha512-Vs55Kq8F+OWvy1RLA31rT+cAyemzgm0EWNeax6BWF8H7QiiOYMJIdcwSDdm5LVgfEkoepsWkS+40+WNb7BUMbg== + "@webassemblyjs/ast@1.7.10": version "1.7.10" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.10.tgz#0cfc61d61286240b72fc522cb755613699eea40a" @@ -2361,10 +2366,10 @@ cordova-lib@8.1.1: which "^1.3.1" xcode "^1.0.0" -cordova-plugin-advanced-http@^2.0.4: - version "2.0.6" - resolved "https://registry.yarnpkg.com/cordova-plugin-advanced-http/-/cordova-plugin-advanced-http-2.0.6.tgz#48c0b3281e1264fb3f92252e1aa39201949ffa22" - integrity sha512-MWPnl0P1z0Bv7O1s9di78tLXq8i3V8TwKqDB7H+583yOnnByTHLd9Fq5senpt37Ifs3c6x3NIgTTR+XBbYGiFg== +cordova-plugin-advanced-http@^2.0.6: + version "2.0.7" + resolved "https://registry.yarnpkg.com/cordova-plugin-advanced-http/-/cordova-plugin-advanced-http-2.0.7.tgz#07b1fdf320f8694eb5cb61de78eeac69e49c0735" + integrity sha512-dG7Vsjc64QPna1jFyqmd+q+Hjj9Cpl4Mz8D2ItKfzgAD3as1U9sTbtdUcaw2AZD/qsK1bkLcRdogTmtKFe8L+g== cordova-plugin-android-permissions@^1.0.0: version "1.0.0" @@ -2391,10 +2396,10 @@ cordova-plugin-ionic-keyboard@^2.1.3: resolved "https://registry.yarnpkg.com/cordova-plugin-ionic-keyboard/-/cordova-plugin-ionic-keyboard-2.1.3.tgz#6b050d5b7cb4393d5758b55f45a46372f25178fe" integrity sha512-6ucQ6FdlLdBm8kJfFnzozmBTjru/0xekHP/dAhjoCZggkGRlgs8TsUJFkxa/bV+qi7Dlo50JjmpE4UMWAO+aOQ== -cordova-plugin-ionic-webview@^2.3.3: - version "2.4.0" - resolved "https://registry.yarnpkg.com/cordova-plugin-ionic-webview/-/cordova-plugin-ionic-webview-2.4.0.tgz#dad20977ebc3a8602bfadddd3dbcc67ac5b7a0db" - integrity sha512-8KQdg0AO0t4pkq/azb14KKLUar3arz+wO7OI7/rbmnL7gt03pfk3Icn66v5wBy8hM2lb8CLmGbdfTcN0f0seiA== +cordova-plugin-ionic-webview@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/cordova-plugin-ionic-webview/-/cordova-plugin-ionic-webview-2.4.1.tgz#102d258c58e83f540fecf6bd010d53cb3c9cc084" + integrity sha512-//Ul3P6LbsglY7NaeSgttcR2KRIJ9XAbUCRWlH5SlZE0JZ6Y5hCyS5VX9Wqct04nwJ+rEF3fHv74h/JyRr/BoA== cordova-plugin-splashscreen@^5.0.2: version "5.0.2" @@ -2406,10 +2411,10 @@ cordova-plugin-statusbar@^2.4.2: resolved "https://registry.yarnpkg.com/cordova-plugin-statusbar/-/cordova-plugin-statusbar-2.4.2.tgz#fc1fbdc0d8d7033a7e8e1f1f7ff167ac9bd4faf6" integrity sha1-/B+9wNjXAzp+jh8ff/FnrJvU+vY= -cordova-plugin-telerik-imagepicker@2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/cordova-plugin-telerik-imagepicker/-/cordova-plugin-telerik-imagepicker-2.2.3.tgz#797652190b40f9bd1b11f363a9f69315ae60cdc9" - integrity sha512-jR8SvH5tou2C5iueqFlH/eU5ZvED4hSLbrymrPfX9NFt+/GuejIRc4Q1pxosdESXJ8xLwZADaGTxgs0e5/IG0g== +cordova-plugin-telerik-imagepicker@^2.2.3: + version "2.3.2" + resolved "https://registry.yarnpkg.com/cordova-plugin-telerik-imagepicker/-/cordova-plugin-telerik-imagepicker-2.3.2.tgz#cd95b23292893f0ca4e5a22b871a664e27a9673c" + integrity sha512-YF98mdjv4jtSvY/nyPj2AQpqSLb2HsulHtQFkznjGkcU9gLeNLQutc48gejsFxL7hMKctQHnIHFi1mj20awqJg== cordova-plugin-whitelist@^1.3.3: version "1.3.3" @@ -2659,7 +2664,7 @@ debug@^3.1.0, debug@^3.2.5: dependencies: ms "^2.1.1" -debug@^4.0.0: +debug@^4.0.0, debug@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -3049,6 +3054,23 @@ engine.io-client@~3.2.0: xmlhttprequest-ssl "~1.5.4" yeast "0.1.2" +engine.io-client@~3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.3.2.tgz#04e068798d75beda14375a264bb3d742d7bc33aa" + integrity sha512-y0CPINnhMvPuwtqXfsGuWE8BB66+B6wTtCofQDRecMQPYX3MYUZXFNKDhdrSe3EVjgOu4V3rxdeqN/Tr91IgbQ== + dependencies: + component-emitter "1.2.1" + component-inherit "0.0.3" + debug "~3.1.0" + engine.io-parser "~2.1.1" + has-cors "1.1.0" + indexof "0.0.1" + parseqs "0.0.5" + parseuri "0.0.5" + ws "~6.1.0" + xmlhttprequest-ssl "~1.5.4" + yeast "0.1.2" + engine.io-parser@~2.1.0, engine.io-parser@~2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.1.3.tgz#757ab970fbf2dfb32c7b74b033216d5739ef79a6" @@ -3072,6 +3094,18 @@ engine.io@~3.2.0: engine.io-parser "~2.1.0" ws "~3.3.1" +engine.io@~3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.3.2.tgz#18cbc8b6f36e9461c5c0f81df2b830de16058a59" + integrity sha512-AsaA9KG7cWPXWHp5FvHdDWY3AMWeZ8x+2pUVLcn71qE5AtAzgGbxuclOytygskw8XGmiQafTmnI9Bix3uihu2w== + dependencies: + accepts "~1.3.4" + base64id "1.0.0" + cookie "0.3.1" + debug "~3.1.0" + engine.io-parser "~2.1.0" + ws "~6.1.0" + enhanced-resolve@4.1.0, enhanced-resolve@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" @@ -7807,6 +7841,26 @@ socket.io-client@2.1.1: socket.io-parser "~3.2.0" to-array "0.1.4" +socket.io-client@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.2.0.tgz#84e73ee3c43d5020ccc1a258faeeb9aec2723af7" + integrity sha512-56ZrkTDbdTLmBIyfFYesgOxsjcLnwAKoN4CiPyTVkMQj3zTUh0QAx3GbvIvLpFEOvQWu92yyWICxB0u7wkVbYA== + dependencies: + backo2 "1.0.2" + base64-arraybuffer "0.1.5" + component-bind "1.0.0" + component-emitter "1.2.1" + debug "~3.1.0" + engine.io-client "~3.3.1" + has-binary2 "~1.0.2" + has-cors "1.1.0" + indexof "0.0.1" + object-component "0.0.3" + parseqs "0.0.5" + parseuri "0.0.5" + socket.io-parser "~3.3.0" + to-array "0.1.4" + socket.io-parser@~3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.2.0.tgz#e7c6228b6aa1f814e6148aea325b51aa9499e077" @@ -7816,6 +7870,15 @@ socket.io-parser@~3.2.0: debug "~3.1.0" isarray "2.0.1" +socket.io-parser@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f" + integrity sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng== + dependencies: + component-emitter "1.2.1" + debug "~3.1.0" + isarray "2.0.1" + socket.io@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.1.1.tgz#a069c5feabee3e6b214a75b40ce0652e1cfb9980" @@ -7828,6 +7891,18 @@ socket.io@2.1.1: socket.io-client "2.1.1" socket.io-parser "~3.2.0" +socket.io@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.2.0.tgz#f0f633161ef6712c972b307598ecd08c9b1b4d5b" + integrity sha512-wxXrIuZ8AILcn+f1B4ez4hJTPG24iNgxBBDaJfT6MsyOhVYiTXWexGoPkd87ktJG8kQEcL/NBvRi64+9k4Kc0w== + dependencies: + debug "~4.1.0" + engine.io "~3.3.1" + has-binary2 "~1.0.2" + socket.io-adapter "~1.1.0" + socket.io-client "2.2.0" + socket.io-parser "~3.3.0" + sockjs-client@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.3.0.tgz#12fc9d6cb663da5739d3dc5fb6e8687da95cb177" @@ -9191,6 +9266,13 @@ ws@~3.3.1: safe-buffer "~5.1.0" ultron "~1.1.0" +ws@~6.1.0: + version "6.1.4" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9" + integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA== + dependencies: + async-limiter "~1.0.0" + xcode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/xcode/-/xcode-1.1.0.tgz#9fcb63f417a9af377bfb743a5c22afce4e1da964"
+ {{ msg.user.name }} {{ msg.createdAt | date:'dd.MM, HH:mm'}} +
{{ msg.body }}
{{ item.annotation }}
{{ item.genre.join(', ') }}