From ce7650bb3bd4479523ee1c7aedf050a8df644b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Fri, 21 May 2021 16:15:50 +0200 Subject: [PATCH 1/7] MOBILE-3320 a11y: Remove aria-label already are explained inside --- src/addons/mod/assign/pages/edit/edit.html | 2 +- .../mod/assign/pages/submission-review/submission-review.html | 2 +- src/addons/mod/data/pages/edit/edit.html | 2 +- src/addons/mod/wiki/pages/edit/edit.html | 2 +- src/addons/mod/workshop/pages/assessment/assessment.html | 2 +- src/addons/mod/workshop/pages/submission/submission.html | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/addons/mod/assign/pages/edit/edit.html b/src/addons/mod/assign/pages/edit/edit.html index 986695272..0b4882748 100644 --- a/src/addons/mod/assign/pages/edit/edit.html +++ b/src/addons/mod/assign/pages/edit/edit.html @@ -8,7 +8,7 @@ - + {{ 'core.save' | translate }} diff --git a/src/addons/mod/assign/pages/submission-review/submission-review.html b/src/addons/mod/assign/pages/submission-review/submission-review.html index c6e25a660..fa5c6d28a 100644 --- a/src/addons/mod/assign/pages/submission-review/submission-review.html +++ b/src/addons/mod/assign/pages/submission-review/submission-review.html @@ -12,7 +12,7 @@ - + {{ 'core.done' | translate }} diff --git a/src/addons/mod/data/pages/edit/edit.html b/src/addons/mod/data/pages/edit/edit.html index 699f9e99d..5d3a38486 100644 --- a/src/addons/mod/data/pages/edit/edit.html +++ b/src/addons/mod/data/pages/edit/edit.html @@ -8,7 +8,7 @@ - + {{ 'core.save' | translate }} diff --git a/src/addons/mod/wiki/pages/edit/edit.html b/src/addons/mod/wiki/pages/edit/edit.html index 29534e664..babf9f585 100644 --- a/src/addons/mod/wiki/pages/edit/edit.html +++ b/src/addons/mod/wiki/pages/edit/edit.html @@ -9,7 +9,7 @@ - + {{ 'core.save' | translate }} diff --git a/src/addons/mod/workshop/pages/assessment/assessment.html b/src/addons/mod/workshop/pages/assessment/assessment.html index 85a95f37a..c4862bb4a 100644 --- a/src/addons/mod/workshop/pages/assessment/assessment.html +++ b/src/addons/mod/workshop/pages/assessment/assessment.html @@ -9,7 +9,7 @@ - + {{ 'core.save' | translate }} diff --git a/src/addons/mod/workshop/pages/submission/submission.html b/src/addons/mod/workshop/pages/submission/submission.html index 33c542f47..96c64cf83 100644 --- a/src/addons/mod/workshop/pages/submission/submission.html +++ b/src/addons/mod/workshop/pages/submission/submission.html @@ -12,7 +12,7 @@ [attr.aria-label]="'core.save' | translate"> {{ 'core.save' | translate }} - + {{ 'core.save' | translate }} From bef5691e15f7acb803f5088e8a62f82523b10511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Fri, 21 May 2021 16:28:38 +0200 Subject: [PATCH 2/7] MOBILE-3320 migration: Remove ionic v4 migration plugin --- .github/workflows/migration.yml | 1 + package-lock.json | 186 -------------------------------- package.json | 1 - 3 files changed, 1 insertion(+), 187 deletions(-) diff --git a/.github/workflows/migration.yml b/.github/workflows/migration.yml index 3e3698ad5..32899b4a6 100644 --- a/.github/workflows/migration.yml +++ b/.github/workflows/migration.yml @@ -15,4 +15,5 @@ jobs: node-version: '12.x' - run: npm ci - run: result=$(find src -type f -iname '*.html' -exec sh -c 'cat {} | tr "\n" " " | grep -Eo "class=\"[^\"]+\"[^>]+class=\"" ' \; | wc -l); test $result -eq 0 + - run: npm install -D @ionic/v4-migration-tslint - run: npx tslint -c ionic-migration.json -p tsconfig.json diff --git a/package-lock.json b/package-lock.json index 118910478..36e7ce61f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3334,16 +3334,6 @@ } } }, - "@ionic/v4-migration-tslint": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@ionic/v4-migration-tslint/-/v4-migration-tslint-1.7.1.tgz", - "integrity": "sha512-1vwBmf0czHvG+vKboxHtYtPJ3Stc7wP8tB2i7qmLslKqMqVFzaTkbNiakt40mHJ/UtbfAzFOkBD6RZZHTWEuzQ==", - "dev": true, - "requires": { - "codelyzer": "^4.4.4", - "tslint": "^5.0.0" - } - }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -5010,12 +5000,6 @@ "picomatch": "^2.0.4" } }, - "app-root-path": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.2.1.tgz", - "integrity": "sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==", - "dev": true - }, "append-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", @@ -6187,12 +6171,6 @@ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "dev": true }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, "builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", @@ -7380,34 +7358,6 @@ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, - "codelyzer": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.5.0.tgz", - "integrity": "sha512-oO6vCkjqsVrEsmh58oNlnJkRXuA30hF8cdNAQV9DytEalDwyOFRvHMnlKFzmOStNerOmPGZU9GAHnBo4tGvtiQ==", - "dev": true, - "requires": { - "app-root-path": "^2.1.0", - "css-selector-tokenizer": "^0.7.0", - "cssauron": "^1.4.0", - "semver-dsl": "^1.0.1", - "source-map": "^0.5.7", - "sprintf-js": "^1.1.1" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true - } - } - }, "collect-v8-coverage": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", @@ -8502,16 +8452,6 @@ "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", "dev": true }, - "css-selector-tokenizer": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz", - "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==", - "dev": true, - "requires": { - "cssesc": "^3.0.0", - "fastparse": "^1.1.2" - } - }, "css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -8536,15 +8476,6 @@ "integrity": "sha512-wHOppVDKl4vTAOWzJt5Ek37Sgd9qq1Bmj/T1OjvicWbU5W7ru7Pqbn0Jdqii3Drx/h+dixHKXNhZYx7blthL7g==", "dev": true }, - "cssauron": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", - "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", - "dev": true, - "requires": { - "through": "X.X.X" - } - }, "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -10500,12 +10431,6 @@ "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", "dev": true }, - "fastparse": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", - "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", - "dev": true - }, "fastq": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", @@ -19850,23 +19775,6 @@ } } }, - "semver-dsl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", - "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", - "dev": true, - "requires": { - "semver": "^5.3.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, "semver-greatest-satisfied-range": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", @@ -21881,100 +21789,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz", "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==" }, - "tslint": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", - "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.29.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - } - } - }, "tsutils": { "version": "3.17.1", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", diff --git a/package.json b/package.json index f21c7bfd7..c5cbebe2e 100644 --- a/package.json +++ b/package.json @@ -137,7 +137,6 @@ "@angular/language-service": "~10.0.0", "@ionic/angular-toolkit": "^2.3.0", "@ionic/cli": "^6.14.1", - "@ionic/v4-migration-tslint": "^1.7.1", "@types/faker": "^5.1.3", "@types/node": "^12.12.64", "@types/resize-observer-browser": "^0.1.5", From 586bda2b69b914fba99616d2bb2c0a7d7b9eb015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 25 May 2021 09:56:18 +0200 Subject: [PATCH 3/7] MOBILE-3320 gestures: Check pointer hasn't moved during long press --- src/core/directives/long-press.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/directives/long-press.ts b/src/core/directives/long-press.ts index 4bc79463e..a92f19c47 100644 --- a/src/core/directives/long-press.ts +++ b/src/core/directives/long-press.ts @@ -27,6 +27,7 @@ export class CoreLongPressDirective implements OnInit, OnDestroy { element: HTMLElement; pressGesture?: Gesture; + protected moved = false; @Output() longPress = new EventEmitter(); @@ -42,8 +43,11 @@ export class CoreLongPressDirective implements OnInit, OnDestroy { this.pressGesture = GestureController.create({ el: this.element, threshold: 0, + disableScroll: true, gestureName: 'longpress', - onEnd: ev => this.longPress.emit(ev.event), + onStart: () => this.moved = false, + onMove: () => this.moved = true, + onEnd: ev => !this.moved && this.longPress.emit(ev.event), }, true); this.pressGesture.enable(); From 02d2905c9a25c9d45bb0322623909e4d02f37f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 25 May 2021 10:02:33 +0200 Subject: [PATCH 4/7] MOBILE-3320 chat: Match styles with messaging --- .../messages/pages/discussion/discussion.scss | 177 +---------------- src/addons/mod/chat/pages/chat/chat.html | 8 +- src/addons/mod/chat/pages/chat/chat.scss | 2 + src/addons/mod/chat/pages/chat/chat.ts | 3 +- .../session-messages/session-messages.html | 4 +- .../session-messages/session-messages.scss | 2 + .../session-messages/session-messages.ts | 2 +- .../send-message-form/send-message-form.scss | 1 + src/theme/components/discussion.scss | 185 ++++++++++++++++++ src/theme/theme.base.scss | 2 +- 10 files changed, 201 insertions(+), 185 deletions(-) create mode 100644 src/theme/components/discussion.scss diff --git a/src/addons/messages/pages/discussion/discussion.scss b/src/addons/messages/pages/discussion/discussion.scss index 42ff565ed..1469d3b7c 100644 --- a/src/addons/messages/pages/discussion/discussion.scss +++ b/src/addons/messages/pages/discussion/discussion.scss @@ -1,25 +1,7 @@ +@import "~theme/components/discussion.scss"; @import "~theme/globals.scss"; :host { - ion-content { - --background: var(--background-alternative); - - &::part(scroll) { - padding-bottom: 0 !important; - } - } - - .addon-messages-discussion-container { - display: flex; - flex-direction: column; - padding-bottom: 15px; - background: var(--background-alternative); - } - - .addon-messages-date { - font-weight: normal; - font-size: 0.9rem; - } .addon-messages-unreadfrom { color: var(--ion-color-primary); @@ -33,157 +15,7 @@ } } - // Message item. - ion-item.addon-message { - border: 0; - border-radius: 4px; - padding: 0 8px 0 8px; - margin: 10px 8px 0 8px; - --background: var(--addon-messages-message-bg); - background: var(--background); - align-self: flex-start; - width: 90%; - max-width: 90%; - --min-height: var(--a11y-min-target-size); - position: relative; - @include core-transition(width); - // This is needed to display bubble tails. - overflow: visible; - - &::part(native) { - --inner-border-width: 0; - --inner-padding-end: 0; - padding: 0; - margin: 0; - } - - core-format-text > p:only-child { - display: inline; - } - - .addon-message-user { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - margin-bottom: .5rem; - margin-top: 0; - color: var(--ion-text-color); - - core-user-avatar { - display: block; - --core-avatar-size: var(--addon-messages-avatar-size); - margin: 0; - } - - div { - font-weight: 500; - flex-grow: 1; - @include padding-horizontal(.5rem); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - } - - ion-note { - color: var(--addon-messages-message-note-text); - font-size: var(--addon-messages-message-note-font-size); - margin: 0; - padding: 0 0 8px 0; - align-self: flex-end; - } - - &:active { - --background: var(--addon-messages-message-activated-bg); - } - - ion-label { - margin: 0; - padding: 8px 0; - } - - .addon-message-text { - display: inline-flex; - * { - color: var(--ion-text-color); - } - } - - .addon-messages-delete-button { - min-height: initial; - line-height: initial; - @include margin(0, null, 0, null); - height: var(--a11y-min-target-size) !important; - align-self: flex-end; - - ion-icon { - font-size: 1.4em; - line-height: initial; - color: var(--ion-color-danger); - } - } - - .tail { - content: ''; - width: 0; - height: 0; - border: 0.5rem solid transparent; - position: absolute; - touch-action: none; - bottom: 0; - } - - // Defines when an item-message is the user's. - &.addon-message-mine { - --background: var(--addon-messages-message-mine-bg); - align-self: flex-end; - - &:active { - --background: var(--addon-messages-message-mine-activated-bg); - } - - .spinner { - @include float(end); - @include margin(2px, -3px, -2px, 5px); - - svg { - width: 16px; - height: 16px; - } - } - - .tail { - @include position(null, -8px, null, null); - @include margin-horizontal(null, -0.5rem); - border-bottom-color: var(--addon-messages-message-mine-bg); - } - - &:active .tail { - border-bottom-color: var(--addon-messages-message-mine-activated-bg); - } - } - - &.addon-message-not-mine .tail { - border-bottom-color: var(--addon-messages-message-bg); - @include position(null, null, null, -8px); - @include margin-horizontal(-0.5rem, null); - } - - &.addon-message-not-mine.activated .tail { - border-bottom-color: var(--addon-messages-message-activated-bg); - } - } - - ion-item.addon-message.addon-message-mine + ion-item.addon-message.addon-message-no-user.addon-message-mine, - ion-item.addon-message.addon-message-not-mine + ion-item.addon-message.addon-message-no-user.addon-message-not-mine { - .item-heading { - margin-bottom: 0; - } - padding-top: 0; - } - - .has-fab .scroll-content { + .has-fab .scroll-content { padding-bottom: 0; } @@ -232,9 +64,4 @@ ion-header ion-toolbar h1 { justify-content: center; } - - ion-footer .toolbar:last-child { - padding-bottom: 4px; - min-height: 0; - } } diff --git a/src/addons/mod/chat/pages/chat/chat.html b/src/addons/mod/chat/pages/chat/chat.html index d741b541e..d88bf4d0b 100644 --- a/src/addons/mod/chat/pages/chat/chat.html +++ b/src/addons/mod/chat/pages/chat/chat.html @@ -86,11 +86,10 @@ [@coreSlideInOut]="message.userid == currentUserId ? '' : 'fromLeft'"> -

- +

+ -
{{ message.userfullname }}
- {{ message.timestamp * 1000 | coreFormatDate: "strftimetime" }} +
{{ message.userfullname }}

@@ -99,6 +98,7 @@

+ {{ message.timestamp * 1000 | coreFormatDate: "strftimetime" }}
diff --git a/src/addons/mod/chat/pages/chat/chat.scss b/src/addons/mod/chat/pages/chat/chat.scss index 2ee6b3c8c..79a0b6be9 100644 --- a/src/addons/mod/chat/pages/chat/chat.scss +++ b/src/addons/mod/chat/pages/chat/chat.scss @@ -1,3 +1,5 @@ +@import "~theme/components/discussion.scss"; + :host { .addon-mod_chat-notice { margin-top: 8px; diff --git a/src/addons/mod/chat/pages/chat/chat.ts b/src/addons/mod/chat/pages/chat/chat.ts index 732beb830..72aaff042 100644 --- a/src/addons/mod/chat/pages/chat/chat.ts +++ b/src/addons/mod/chat/pages/chat/chat.ts @@ -36,7 +36,7 @@ import { AddonModChatFormattedMessage, AddonModChatHelper } from '../../services selector: 'page-addon-mod-chat-chat', templateUrl: 'chat.html', animations: [CoreAnimations.SLIDE_IN_OUT], - styleUrls: ['chat.scss', '../../../../messages/pages/discussion/discussion.scss'], + styleUrls: ['chat.scss'], }) export class AddonModChatChatPage implements OnInit, OnDestroy, CanLeave { @@ -54,7 +54,6 @@ export class AddonModChatChatPage implements OnInit, OnDestroy, CanLeave { courseId!: number; cmId!: number; - protected logger; protected chatId!: number; protected sessionId?: string; protected lastTime = 0; diff --git a/src/addons/mod/chat/pages/session-messages/session-messages.html b/src/addons/mod/chat/pages/session-messages/session-messages.html index 4fbc6d40a..f9706469e 100644 --- a/src/addons/mod/chat/pages/session-messages/session-messages.html +++ b/src/addons/mod/chat/pages/session-messages/session-messages.html @@ -11,7 +11,7 @@ - +
@@ -83,7 +83,6 @@
{{ message.userfullname }}
- {{ message.timestamp * 1000 | coreFormatDate: "strftimetime" }}

@@ -91,6 +90,7 @@ [courseId]="courseId">

+ {{ message.timestamp * 1000 | coreFormatDate: "strftimetime" }}
diff --git a/src/addons/mod/chat/pages/session-messages/session-messages.scss b/src/addons/mod/chat/pages/session-messages/session-messages.scss index 2ee6b3c8c..79a0b6be9 100644 --- a/src/addons/mod/chat/pages/session-messages/session-messages.scss +++ b/src/addons/mod/chat/pages/session-messages/session-messages.scss @@ -1,3 +1,5 @@ +@import "~theme/components/discussion.scss"; + :host { .addon-mod_chat-notice { margin-top: 8px; diff --git a/src/addons/mod/chat/pages/session-messages/session-messages.ts b/src/addons/mod/chat/pages/session-messages/session-messages.ts index 27620d907..00b7e8a71 100644 --- a/src/addons/mod/chat/pages/session-messages/session-messages.ts +++ b/src/addons/mod/chat/pages/session-messages/session-messages.ts @@ -28,7 +28,7 @@ import { AddonModChatFormattedSessionMessage, AddonModChatHelper } from '../../s @Component({ selector: 'page-addon-mod-chat-session-messages', templateUrl: 'session-messages.html', - styleUrls: ['session-messages.scss', '../../../../messages/pages/discussion/discussion.scss'], + styleUrls: ['session-messages.scss'], }) export class AddonModChatSessionMessagesPage implements OnInit { diff --git a/src/core/components/send-message-form/send-message-form.scss b/src/core/components/send-message-form/send-message-form.scss index c9500e34a..ce52b352e 100644 --- a/src/core/components/send-message-form/send-message-form.scss +++ b/src/core/components/send-message-form/send-message-form.scss @@ -20,5 +20,6 @@ line-height: 20px; padding: 9px 12px 11px; margin: 5px 10px; + resize: vertical; } } diff --git a/src/theme/components/discussion.scss b/src/theme/components/discussion.scss new file mode 100644 index 000000000..66f450ba9 --- /dev/null +++ b/src/theme/components/discussion.scss @@ -0,0 +1,185 @@ +@import "~theme/globals.scss"; + +:host { + ion-content { + --background: var(--background-alternative); + + &::part(scroll) { + padding-bottom: 0 !important; + } + } + + .addon-messages-discussion-container { + display: flex; + flex-direction: column; + padding-bottom: 15px; + background: var(--background-alternative); + } + + .addon-messages-date { + font-weight: normal; + font-size: 0.9rem; + } + + // Message item. + ion-item.addon-message { + border: 0; + border-radius: 4px; + padding: 0 8px 0 8px; + margin: 10px 8px 0 8px; + --background: var(--addon-messages-message-bg); + background: var(--background); + align-self: flex-start; + width: 90%; + max-width: 90%; + --min-height: var(--a11y-min-target-size); + position: relative; + @include core-transition(width); + // This is needed to display bubble tails. + overflow: visible; + + &::part(native) { + --inner-border-width: 0; + --inner-padding-end: 0; + padding: 0; + margin: 0; + } + + core-format-text > p:only-child { + display: inline; + } + + .addon-message-user { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + margin-bottom: .5rem; + margin-top: 0; + color: var(--ion-text-color); + + core-user-avatar { + display: block; + --core-avatar-size: var(--addon-messages-avatar-size); + margin: 0; + } + + div { + font-weight: 500; + flex-grow: 1; + @include padding-horizontal(.5rem); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + ion-note { + color: var(--addon-messages-message-note-text); + font-size: var(--addon-messages-message-note-font-size); + margin: 0; + padding: 0 0 8px 0; + align-self: flex-end; + } + + &[tappable]:active { + --background: var(--addon-messages-message-activated-bg); + } + + ion-label { + margin: 0; + padding: 8px 0; + } + + .addon-message-text { + display: inline-flex; + * { + color: var(--ion-text-color); + } + } + + .tail { + content: ''; + width: 0; + height: 0; + border: 0.5rem solid transparent; + position: absolute; + touch-action: none; + bottom: 0; + } + + // Defines when an item-message is the user's. + &.addon-message-mine { + --background: var(--addon-messages-message-mine-bg); + align-self: flex-end; + + &[tappable]:active { + --background: var(--addon-messages-message-mine-activated-bg); + } + + .spinner { + @include float(end); + @include margin(2px, -3px, -2px, 5px); + + svg { + width: 16px; + height: 16px; + } + } + + .tail { + @include position(null, -8px, null, null); + @include margin-horizontal(null, -0.5rem); + border-bottom-color: var(--addon-messages-message-mine-bg); + } + + &[tappable]:active .tail { + border-bottom-color: var(--addon-messages-message-mine-activated-bg); + } + } + + &.addon-message-not-mine .tail { + border-bottom-color: var(--addon-messages-message-bg); + @include position(null, null, null, -8px); + @include margin-horizontal(-0.5rem, null); + } + + &[tappable].addon-message-not-mine.activated .tail { + border-bottom-color: var(--addon-messages-message-activated-bg); + } + + .addon-messages-delete-button { + min-height: initial; + line-height: initial; + @include margin(0, null, 0, null); + height: var(--a11y-min-target-size) !important; + align-self: flex-end; + + ion-icon { + font-size: 1.4em; + line-height: initial; + color: var(--ion-color-danger); + } + } + + &.addon-message-no-user { + margin-top: 8px; + } + } + + ion-item.addon-message.addon-message-mine + ion-item.addon-message.addon-message-no-user.addon-message-mine, + ion-item.addon-message.addon-message-not-mine + ion-item.addon-message.addon-message-no-user.addon-message-not-mine { + .item-heading { + margin-bottom: 0; + } + padding-top: 0; + } + +} + +:host-context(.ios) { + ion-footer .toolbar:last-child { + padding-bottom: 4px; + min-height: 0; + } +} diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss index f2e7f1458..d088270be 100644 --- a/src/theme/theme.base.scss +++ b/src/theme/theme.base.scss @@ -364,7 +364,7 @@ ion-toolbar { .item-dimmed { opacity: 0.7; - --background: var(--gray-lighter); + --background: var(--ion-color-light); } // Extra text colors. From e7d4588bcf2f79bca3dd8aebf66f1c0aad00ec5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 25 May 2021 13:15:57 +0200 Subject: [PATCH 5/7] MOBILE-3320 comments: Minor fixes on comments list --- src/core/features/comments/pages/viewer/viewer.page.ts | 7 ++++--- src/core/features/comments/services/comments.ts | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/core/features/comments/pages/viewer/viewer.page.ts b/src/core/features/comments/pages/viewer/viewer.page.ts index 475b6ebad..1789a611e 100644 --- a/src/core/features/comments/pages/viewer/viewer.page.ts +++ b/src/core/features/comments/pages/viewer/viewer.page.ts @@ -153,7 +153,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { let comments = commentsResponse.comments.sort((a, b) => b.timecreated - a.timecreated); if (typeof commentsResponse.count != 'undefined') { - this.canLoadMore = (this.comments.length + comments.length) > commentsResponse.count; + this.canLoadMore = (this.comments.length + comments.length) < commentsResponse.count; } else { // Old style. this.canLoadMore = commentsResponse.comments.length > 0 && @@ -349,8 +349,9 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { const deletedOnline = await CoreComments.deleteComment(deleteComment); this.showDelete = false; - if (deletedOnline) { - const index = this.comments.findIndex((comment) => comment.id == comment.id); + if (deletedOnline && 'id' in comment) { + const index = this.comments.findIndex((commentinList) => commentinList.id == comment.id); + if (index >= 0) { this.comments.splice(index, 1); diff --git a/src/core/features/comments/services/comments.ts b/src/core/features/comments/services/comments.ts index e87d2e3b5..1c252e21c 100644 --- a/src/core/features/comments/services/comments.ts +++ b/src/core/features/comments/services/comments.ts @@ -85,11 +85,11 @@ export class CoreCommentsProvider { itemId: number, area: string = '', siteId?: string, - ): Promise { + ): Promise { siteId = siteId || CoreSites.getCurrentSiteId(); // Convenience function to store a comment to be synchronized later. - const storeOffline = async (): Promise => { + const storeOffline = async (): Promise => { await CoreCommentsOffline.saveComment(content, contextLevel, instanceId, component, itemId, area, siteId); return false; From 5a15d1722f0b22a98937d5304c31821b1560f78a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 25 May 2021 13:18:46 +0200 Subject: [PATCH 6/7] MOBILE-3320 comments: Show comments as messages --- .../comments/components/add/add-modal.ts | 95 --------- .../features/comments/components/add/add.html | 26 --- .../comments/components/components.module.ts | 3 - .../comments/pages/viewer/viewer.html | 144 +++++++------ .../comments/pages/viewer/viewer.page.ts | 198 +++++++++++++----- .../comments/pages/viewer/viewer.scss | 1 + 6 files changed, 226 insertions(+), 241 deletions(-) delete mode 100644 src/core/features/comments/components/add/add-modal.ts delete mode 100644 src/core/features/comments/components/add/add.html create mode 100644 src/core/features/comments/pages/viewer/viewer.scss diff --git a/src/core/features/comments/components/add/add-modal.ts b/src/core/features/comments/components/add/add-modal.ts deleted file mode 100644 index 5d1a0a224..000000000 --- a/src/core/features/comments/components/add/add-modal.ts +++ /dev/null @@ -1,95 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, ViewChild, ElementRef, Input } from '@angular/core'; -import { CoreComments } from '@features/comments/services/comments'; -import { CoreApp } from '@services/app'; -import { CoreSites } from '@services/sites'; -import { CoreDomUtils } from '@services/utils/dom'; -import { CoreForms } from '@singletons/form'; -import { ModalController } from '@singletons'; - -/** - * Component that displays a text area for composing a comment. - */ -@Component({ - selector: 'core-comments-add', - templateUrl: 'add.html', -}) -export class CoreCommentsAddComponent { - - @ViewChild('commentForm') formElement?: ElementRef; - - @Input() protected contextLevel!: string; - @Input() protected instanceId!: number; - @Input() protected componentName!: string; - @Input() protected itemId!: number; - @Input() protected area = ''; - @Input() content = ''; - - processing = false; - - /** - * Send the comment or store it offline. - * - * @param e Event. - */ - async addComment(e: Event): Promise { - e.preventDefault(); - e.stopPropagation(); - - CoreApp.closeKeyboard(); - const loadingModal = await CoreDomUtils.showModalLoading('core.sending', true); - // Freeze the add comment button. - this.processing = true; - try { - const commentsResponse = await CoreComments.addComment( - this.content, - this.contextLevel, - this.instanceId, - this.componentName, - this.itemId, - this.area, - ); - - CoreForms.triggerFormSubmittedEvent( - this.formElement, - !!commentsResponse, - CoreSites.getCurrentSiteId(), - ); - - ModalController.dismiss(commentsResponse).finally(() => { - CoreDomUtils.showToast( - commentsResponse ? 'core.comments.eventcommentcreated' : 'core.datastoredoffline', - true, - 3000, - ); - }); - } catch (error) { - CoreDomUtils.showErrorModal(error); - this.processing = false; - } finally { - loadingModal.dismiss(); - } - } - - /** - * Close modal. - */ - closeModal(): void { - CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId()); - ModalController.dismiss(); - } - -} diff --git a/src/core/features/comments/components/add/add.html b/src/core/features/comments/components/add/add.html deleted file mode 100644 index 45d8f93e6..000000000 --- a/src/core/features/comments/components/add/add.html +++ /dev/null @@ -1,26 +0,0 @@ - - -

{{ 'core.comments.addcomment' | translate }}

- - - - - -
-
- -
- - - - - - -
- - {{ 'core.comments.savecomment' | translate }} - -
-
-
diff --git a/src/core/features/comments/components/components.module.ts b/src/core/features/comments/components/components.module.ts index 7cbec08f7..2a2c672fd 100644 --- a/src/core/features/comments/components/components.module.ts +++ b/src/core/features/comments/components/components.module.ts @@ -14,20 +14,17 @@ import { CoreSharedModule } from '@/core/shared.module'; import { NgModule } from '@angular/core'; -import { CoreCommentsAddComponent } from './add/add-modal'; import { CoreCommentsCommentsComponent } from './comments/comments'; @NgModule({ declarations: [ CoreCommentsCommentsComponent, - CoreCommentsAddComponent, ], imports: [ CoreSharedModule, ], exports: [ CoreCommentsCommentsComponent, - CoreCommentsAddComponent, ], }) export class CoreCommentsComponentsModule {} diff --git a/src/core/features/comments/pages/viewer/viewer.html b/src/core/features/comments/pages/viewer/viewer.html index 7f48bfdd6..6248c094e 100644 --- a/src/core/features/comments/pages/viewer/viewer.html +++ b/src/core/features/comments/pages/viewer/viewer.html @@ -34,75 +34,95 @@ [message]="'core.comments.nocomments' | translate"> - - - - - {{ 'core.thereisdatatosync' | translate:{$a: 'core.comments.comments' | translate | lowercase } }} - - - + + + - - - + + + +

+ {{ comment.timecreated * 1000 | coreFormatDate: "strftimedayshort" }} +

+ + + + +

+ + +
{{ comment.fullname }}
+

+ +

+ + +

+
+ + + {{ comment.timecreated * 1000 | coreFormatDate: 'strftimetime' }} + + + + {{ 'core.deletedoffline' | translate }} + + + +
+ + + + + + +
+
+ + -

{{ offlineComment.fullname }}

-

- {{ 'core.notsent' | translate }} + +

+ + {{ 'core.thereisdatatosync' | translate:{$a: 'core.comments.comments' | translate | lowercase } }} +

+ +

+ +

- + + {{ 'core.notsent' | translate }} + +
+
- - - - - - -
+ - - - - -

{{ comment.fullname }}

-

{{ comment.timecreated * 1000 | coreFormatDate: 'strftimerecentfull' }}

-

- - {{ 'core.deletedoffline' | translate }} - -

-
- - - - - - -
- - - - - - -
- - - - - - - - + + + + + + diff --git a/src/core/features/comments/pages/viewer/viewer.page.ts b/src/core/features/comments/pages/viewer/viewer.page.ts index 1789a611e..f0522caca 100644 --- a/src/core/features/comments/pages/viewer/viewer.page.ts +++ b/src/core/features/comments/pages/viewer/viewer.page.ts @@ -15,7 +15,7 @@ import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { CoreAnimations } from '@components/animations'; -import { ActivatedRoute, Params } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { CoreSites } from '@services/sites'; import { CoreComments, @@ -33,13 +33,14 @@ import { CoreNavigator } from '@services/navigator'; import { Translate } from '@singletons'; import { CoreUtils } from '@services/utils/utils'; import { CoreDomUtils } from '@services/utils/dom'; -import { CoreUser, CoreUserProfile } from '@features/user/services/user'; +import { CoreUser } from '@features/user/services/user'; import { CoreTextUtils } from '@services/utils/text'; import { CoreError } from '@classes/errors/error'; import { CoreCommentsOffline } from '@features/comments/services/comments-offline'; import { CoreCommentsDBRecord } from '@features/comments/services/database/comments'; import { CoreTimeUtils } from '@services/utils/time'; -import { CoreCommentsAddComponent } from '@features/comments/components/add/add-modal'; +import { CoreApp } from '@services/app'; +import moment from 'moment'; /** * Page that displays comments. @@ -48,12 +49,13 @@ import { CoreCommentsAddComponent } from '@features/comments/components/add/add- selector: 'page-core-comments-viewer', templateUrl: 'viewer.html', animations: [CoreAnimations.SLIDE_IN_OUT], + styleUrls: ['viewer.scss'], }) export class CoreCommentsViewerPage implements OnInit, OnDestroy { @ViewChild(IonContent) content?: IonContent; - comments: CoreCommentsDataWithUser[] = []; + comments: CoreCommentsDataToDisplay[] = []; commentsLoaded = false; contextLevel!: ContextLevel; instanceId!: number; @@ -73,10 +75,12 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { syncIcon = CoreConstants.ICON_LOADING; offlineComment?: CoreCommentsOfflineWithUser; currentUserId: number; + sending = false; + newComment = ''; protected addDeleteCommentsAvailable = false; protected syncObserver?: CoreEventObserver; - protected currentUser?: CoreUserProfile; + protected viewDestroyed = false; constructor( protected route: ActivatedRoute, @@ -95,8 +99,6 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { this.refreshIcon = CoreConstants.ICON_LOADING; this.syncIcon = CoreConstants.ICON_LOADING; - this.content?.scrollToTop(); - this.page = 0; this.comments = []; this.fetchComments(false); @@ -151,7 +153,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { ); this.canAddComments = this.addDeleteCommentsAvailable && !!commentsResponse.canpost; - let comments = commentsResponse.comments.sort((a, b) => b.timecreated - a.timecreated); + let comments = commentsResponse.comments.sort((a, b) => a.timecreated - b.timecreated); if (typeof commentsResponse.count != 'undefined') { this.canLoadMore = (this.comments.length + comments.length) < commentsResponse.count; } else { @@ -162,7 +164,13 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { comments = await Promise.all(comments.map((comment) => this.loadCommentProfile(comment))); - this.comments = this.comments.concat(comments); + this.comments = comments.concat(this.comments); + + this.comments.forEach((comment, index) => { + comment.showDate = this.showDate(comment, this.comments[index - 1]); + comment.showUserData = this.showUserData(comment, this.comments[index - 1]); + comment.showTail = this.showTail(comment, this.comments[index + 1]); + }); this.canDeleteComments = this.addDeleteCommentsAvailable && (this.hasOffline || this.comments.some((comment) => !!comment.delete)); @@ -179,6 +187,10 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { this.commentsLoaded = true; this.refreshIcon = CoreConstants.ICON_REFRESH; this.syncIcon = CoreConstants.ICON_SYNC; + + if (this.page == 0) { + this.scrollToBottom(); + } } } @@ -189,7 +201,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading. * @return Resolved when done. */ - loadMore(infiniteComplete?: () => void): Promise { + loadPrevious(infiniteComplete?: () => void): Promise { this.page++; this.canLoadMore = false; @@ -262,48 +274,64 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { } /** - * Add a new comment to the list. + * Send the comment or store it offline. * - * @param e Event. + * @param text Comment text to add. */ - async addComment(e: Event): Promise { - e.preventDefault(); - e.stopPropagation(); + async addComment(text: string): Promise { + CoreApp.closeKeyboard(); + const loadingModal = await CoreDomUtils.showModalLoading('core.sending', true); + // Freeze the add comment button. + this.sending = true; + try { + const commentsResponse = await CoreComments.addComment( + text, + this.contextLevel, + this.instanceId, + this.componentName, + this.itemId, + this.area, + ); - const params: Params = { - contextLevel: this.contextLevel, - instanceId: this.instanceId, - componentName: this.componentName, - itemId: this.itemId, - area: this.area, - content: this.offlineComment ? this.offlineComment!.content : '', - }; + CoreDomUtils.showToast( + commentsResponse ? 'core.comments.eventcommentcreated' : 'core.datastoredoffline', + true, + 3000, + ); - const comment = await CoreDomUtils.openModal({ - component: CoreCommentsAddComponent, - componentProps: params, - }); + if (commentsResponse) { + this.invalidateComments(); - if (comment) { - this.invalidateComments(); + const addedComments = await this.loadCommentProfile(commentsResponse); + addedComments.showDate = this.showDate(addedComments, this.comments[this.comments.length - 1]); + addedComments.showUserData = this.showUserData(addedComments, this.comments[this.comments.length - 1]); + addedComments.showTail = this.showTail(addedComments, this.comments[this.comments.length + 1]); - const addedComments = await this.loadCommentProfile(comment); - // Add the comment to the top. - this.comments = [addedComments].concat(this.comments); - this.canDeleteComments = this.addDeleteCommentsAvailable; + // Add the comment to the top. + this.comments = this.comments.concat([addedComments]); + this.canDeleteComments = this.addDeleteCommentsAvailable; - CoreEvents.trigger(CoreCommentsProvider.COMMENTS_COUNT_CHANGED_EVENT, { - contextLevel: this.contextLevel, - instanceId: this.instanceId, - component: this.componentName, - itemId: this.itemId, - area: this.area, - countChange: 1, - }, CoreSites.getCurrentSiteId()); + CoreEvents.trigger(CoreCommentsProvider.COMMENTS_COUNT_CHANGED_EVENT, { + contextLevel: this.contextLevel, + instanceId: this.instanceId, + component: this.componentName, + itemId: this.itemId, + area: this.area, + countChange: 1, + }, CoreSites.getCurrentSiteId()); - } else if (comment === false) { - // Comments added in offline mode. - return this.loadOfflineData(); + } else if (commentsResponse === false) { + // Comments added in offline mode. + await this.loadOfflineData(); + } + } catch (error) { + CoreDomUtils.showErrorModal(error); + } finally { + loadingModal.dismiss(); + this.sending = false; + + // New comments. + this.scrollToBottom(); } } @@ -313,7 +341,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { * @param e Click event. * @param comment Comment to delete. */ - async deleteComment(e: Event, comment: CoreCommentsDataWithUser | CoreCommentsOfflineWithUser): Promise { + async deleteComment(e: Event, comment: CoreCommentsDataToDisplay | CoreCommentsOfflineWithUser): Promise { e.preventDefault(); e.stopPropagation(); @@ -397,7 +425,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { * @param comment Comment object. * @return Promise resolved with modified comment when done. */ - protected async loadCommentProfile(comment: CoreCommentsDataWithUser): Promise { + protected async loadCommentProfile(comment: CoreCommentsDataToDisplay): Promise { // Get the user profile image. try { const user = await CoreUser.getProfile(comment.userid!, undefined, true); @@ -411,6 +439,54 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { } + /** + * Check if the user info should be displayed for the current message. + * User data is only displayed if the previous message was from another user. + * + * @param comment Comment object. + * @param prevComment Previous comment object. + * @return Whether user data should be shown. + */ + protected showUserData( + comment: CoreCommentsDataToDisplay, + prevComment?: CoreCommentsDataToDisplay, + ): boolean { + return comment.userid != this.currentUserId && (!prevComment || prevComment.userid != comment.userid || !!comment.showDate); + } + + /** + * Check if a css tail should be shown. + * + * @param comment Comment object. + * @param nextComment Previous comment object. + * @return Whether user data should be shown. + */ + protected showTail( + comment: CoreCommentsDataToDisplay, + nextComment?: CoreCommentsDataToDisplay, + ): boolean { + return !nextComment || nextComment.userid != comment.userid || !!nextComment.showDate; + } + + /** + * Check if the date should be displayed between messages (when the day changes at midnight for example). + * + * @param comment Comment object. + * @param prevComment Previous comment object. + * @return True if messages are from diferent days, false othetwise. + */ + protected showDate( + comment: CoreCommentsDataToDisplay, + prevComment?: CoreCommentsDataToDisplay, + ): boolean { + if (!prevComment) { + return true; + } + + // Check if day has changed. + return !moment(comment.timecreated * 1000).isSame(prevComment.timecreated * 1000, 'day'); + } + /** * Load offline comments. * @@ -434,14 +510,10 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { return; } - if (!this.currentUser) { - this.currentUser = await CoreUser.getProfile(this.currentUserId, undefined, true); + if (this.newComment == '') { + this.newComment = this.offlineComment!.content; } - if (this.currentUser) { - this.offlineComment!.profileimageurl = this.currentUser.profileimageurl; - this.offlineComment!.fullname = this.currentUser.fullname; - } this.offlineComment!.userid = this.currentUserId; return; @@ -481,7 +553,7 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { * @param e Click event. * @param comment Comment to delete. */ - async undoDeleteComment(e: Event, comment: CoreCommentsDataWithUser): Promise { + async undoDeleteComment(e: Event, comment: CoreCommentsDataToDisplay): Promise { e.preventDefault(); e.stopPropagation(); @@ -491,6 +563,18 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { this.showDelete = false; } + /** + * Scroll bottom when render has finished. + */ + protected scrollToBottom(): void { + // Need a timeout to leave time to the view to be rendered. + setTimeout(() => { + if (!this.viewDestroyed) { + this.content?.scrollToBottom(); + } + }, 100); + } + /** * Toggle delete. */ @@ -502,15 +586,19 @@ export class CoreCommentsViewerPage implements OnInit, OnDestroy { * Page destroyed. */ ngOnDestroy(): void { - this.syncObserver && this.syncObserver.off(); + this.syncObserver?.off(); + this.viewDestroyed = true; } } -export type CoreCommentsDataWithUser = CoreCommentsData & { +export type CoreCommentsDataToDisplay = CoreCommentsData & { profileimageurl?: string; fullname?: string; deleted?: boolean; + showDate?: boolean; + showTail?: boolean; + showUserData?: boolean; }; export type CoreCommentsOfflineWithUser = CoreCommentsDBRecord & { diff --git a/src/core/features/comments/pages/viewer/viewer.scss b/src/core/features/comments/pages/viewer/viewer.scss new file mode 100644 index 000000000..7fb2d0cc4 --- /dev/null +++ b/src/core/features/comments/pages/viewer/viewer.scss @@ -0,0 +1 @@ +@import "~theme/components/discussion.scss"; From 871a36f7a2cba4e935919910fa0cfbb266c4b4e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 25 May 2021 14:26:32 +0200 Subject: [PATCH 7/7] MOBILE-3320 core: Prevent new line to be shown before submit --- .../core-send-message-form.html | 20 +++++++++++++--- .../send-message-form/send-message-form.ts | 23 ++++++++++++++++++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/core/components/send-message-form/core-send-message-form.html b/src/core/components/send-message-form/core-send-message-form.html index 066072909..292f9c2f2 100644 --- a/src/core/components/send-message-form/core-send-message-form.html +++ b/src/core/components/send-message-form/core-send-message-form.html @@ -1,7 +1,21 @@
- + diff --git a/src/core/components/send-message-form/send-message-form.ts b/src/core/components/send-message-form/send-message-form.ts index aaa69d383..a783732ad 100644 --- a/src/core/components/send-message-form/send-message-form.ts +++ b/src/core/components/send-message-form/send-message-form.ts @@ -103,13 +103,34 @@ export class CoreSendMessageFormComponent implements OnInit { this.onResize.emit(); } + /** + * A11y key functionality that prevents keyDown events. + * + * @param e Event. + */ + enterKeyDown(e: KeyboardEvent, other?: string): void { + if (this.sendDisabled) { + return; + } + + if (this.sendOnEnter && !other) { + // Enter clicked, send the message. + e.preventDefault(); + e.stopPropagation(); + } else if (!this.sendOnEnter && !CoreApp.isMobile() && other == 'control') { + // Cmd+Enter or Ctrl+Enter, send message. + e.preventDefault(); + e.stopPropagation(); + } + } + /** * Enter key clicked. * * @param e Event. * @param other The name of the other key that was clicked, undefined if no other key. */ - enterClicked(e: Event, other?: string): void { + enterKeyUp(e: Event, other?: string): void { if (this.sendDisabled) { return; }