Handle soft keyboard better

This commit is contained in:
N-Pex 2020-12-04 10:44:46 +01:00
parent bf0f2af59e
commit 70d015e5ce
4 changed files with 93 additions and 30 deletions

15
package-lock.json generated
View file

@ -20,6 +20,7 @@
"roboto-fontface": "*", "roboto-fontface": "*",
"v-emoji-picker": "^2.3.1", "v-emoji-picker": "^2.3.1",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-resize": "^0.5.0",
"vue-router": "^3.2.0", "vue-router": "^3.2.0",
"vuetify": "^2.2.11", "vuetify": "^2.2.11",
"vuex": "^3.5.1" "vuex": "^3.5.1"
@ -13499,6 +13500,14 @@
"vue-class-component": "*" "vue-class-component": "*"
} }
}, },
"node_modules/vue-resize": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-0.5.0.tgz",
"integrity": "sha512-k5gOOwTIGSoWZb133Gx3IfSeiiAmve5GyWI7+pU8CvbNntpSAlrCYwZ26GB63NpxcLPK94+m0BDl5TxuZUI+Hw==",
"peerDependencies": {
"vue": "^2.6.11"
}
},
"node_modules/vue-router": { "node_modules/vue-router": {
"version": "3.4.9", "version": "3.4.9",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.9.tgz", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.9.tgz",
@ -25652,6 +25661,12 @@
"integrity": "sha512-xYA8MkZynPBGd/w5QFJ2d/NM0z/YeegMqYTphy7NJQXbZcuU6FC6AOdUAcy4SXP+YnkerC6AfH+ldg7PDk9ESQ==", "integrity": "sha512-xYA8MkZynPBGd/w5QFJ2d/NM0z/YeegMqYTphy7NJQXbZcuU6FC6AOdUAcy4SXP+YnkerC6AfH+ldg7PDk9ESQ==",
"requires": {} "requires": {}
}, },
"vue-resize": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-0.5.0.tgz",
"integrity": "sha512-k5gOOwTIGSoWZb133Gx3IfSeiiAmve5GyWI7+pU8CvbNntpSAlrCYwZ26GB63NpxcLPK94+m0BDl5TxuZUI+Hw==",
"requires": {}
},
"vue-router": { "vue-router": {
"version": "3.4.9", "version": "3.4.9",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.9.tgz", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.9.tgz",

View file

@ -21,6 +21,7 @@
"roboto-fontface": "*", "roboto-fontface": "*",
"v-emoji-picker": "^2.3.1", "v-emoji-picker": "^2.3.1",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-resize": "^0.5.0",
"vue-router": "^3.2.0", "vue-router": "^3.2.0",
"vuetify": "^2.2.11", "vuetify": "^2.2.11",
"vuex": "^3.5.1" "vuex": "^3.5.1"

View file

@ -7,9 +7,18 @@
v-on:sscroll="onScroll" v-on:sscroll="onScroll"
> >
<div v-for="event in events" :key="event.getId()"> <div v-for="event in events" :key="event.getId()">
<div v-if="!event.isRelation() && !event.isRedacted() && !event.isRedaction()"> <div
<div style="position:relative;user-select:none" v-if="
v-on:touchstart="(e) => { touchStart(e, event) }" !event.isRelation() && !event.isRedacted() && !event.isRedaction()
"
>
<div
style="position: relative; user-select: none"
v-on:touchstart="
(e) => {
touchStart(e, event);
}
"
v-on:touchend="touchEnd" v-on:touchend="touchEnd"
v-on:touchcancel="touchCancel" v-on:touchcancel="touchCancel"
v-on:touchmove="touchMove" v-on:touchmove="touchMove"
@ -37,6 +46,12 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Handle resizes, e.g. when soft keyboard is shown/hidden -->
<resize-observer
ref="chatContainerResizer"
@notify="handleChatContainerResize"
/>
</div> </div>
<!-- Input area --> <!-- Input area -->
@ -196,7 +211,13 @@ export default {
currentSendError: null, currentSendError: null,
showEmojiPicker: false, showEmojiPicker: false,
selectedEvent: null, selectedEvent: null,
showContextMenu: false showContextMenu: false,
/**
* Current chat container size. We need to keep track of this so that if and when
* a soft keyboard is shown/hidden we can restore the scroll position correctly.
* If we don't, the keyboard will simply overflow the message we are answering to etc.
*/
chatContainerSize: 0,
}; };
}, },
@ -205,6 +226,8 @@ export default {
this.scrollPosition = new ScrollPosition(container); this.scrollPosition = new ScrollPosition(container);
this.$matrix.on("Room.timeline", this.onEvent); this.$matrix.on("Room.timeline", this.onEvent);
this.$matrix.on("RoomMember.typing", this.onUserTyping); this.$matrix.on("RoomMember.typing", this.onUserTyping);
this.chatContainerSize = this.$refs.chatContainerResizer.$el.clientHeight;
console.log("resize initial height: ", this.chatContainerSize);
}, },
destroyed() { destroyed() {
@ -258,13 +281,13 @@ export default {
methods: { methods: {
touchX(event) { touchX(event) {
if(event.type.indexOf('mouse') !== -1){ if (event.type.indexOf("mouse") !== -1) {
return event.clientX; return event.clientX;
} }
return event.touches[0].clientX; return event.touches[0].clientX;
}, },
touchY(event) { touchY(event) {
if(event.type.indexOf('mouse') !== -1){ if (event.type.indexOf("mouse") !== -1) {
return event.clientY; return event.clientY;
} }
return event.touches[0].clientY; return event.touches[0].clientY;
@ -291,17 +314,38 @@ export default {
this.touchCurrentX = this.touchX(e); this.touchCurrentX = this.touchX(e);
this.touchCurrentY = this.touchY(e); this.touchCurrentY = this.touchY(e);
var tapTolerance = 4; var tapTolerance = 4;
var touchMoved = Math.abs(this.touchStartX - this.touchCurrentX) > tapTolerance || var touchMoved =
Math.abs(this.touchStartX - this.touchCurrentX) > tapTolerance ||
Math.abs(this.touchStartY - this.touchCurrentY) > tapTolerance; Math.abs(this.touchStartY - this.touchCurrentY) > tapTolerance;
if (touchMoved) { if (touchMoved) {
this.touchTimer && clearTimeout(this.touchTimer); this.touchTimer && clearTimeout(this.touchTimer);
} }
}, },
/**
* Triggered when out "long tap" timer hits.
*/
touchTimerElapsed() { touchTimerElapsed() {
console.log('timer');
this.showContextMenu = true; this.showContextMenu = true;
}, },
/**
* If chat container is shrunk (probably because soft keyboard is shown) adjust
* the scroll position so that e.g. if we were looking at the last message when
* moving focus to the input field, we would still see the last message. Otherwise
* if would be hidden behind the keyboard.
*/
handleChatContainerResize({ width, height }) {
console.log("resized", width, height);
const delta = height - this.chatContainerSize;
this.chatContainerSize = height;
console.log("resized delta " + delta);
const container = this.$refs.chatContainer;
if (delta < 0) {
container.scrollTop -= delta;
}
},
componentForEvent(event) { componentForEvent(event) {
switch (event.getType()) { switch (event.getType()) {
case "m.room.member": case "m.room.member":
@ -372,7 +416,7 @@ export default {
// If we are at bottom, scroll to see new events... // If we are at bottom, scroll to see new events...
const container = this.$refs.chatContainer; const container = this.$refs.chatContainer;
var scrollToSeeNew = (event.getSender() == this.$matrix.currentUserId); // When we sent, scroll var scrollToSeeNew = event.getSender() == this.$matrix.currentUserId; // When we sent, scroll
if ( if (
container.scrollHeight - container.scrollTop.toFixed(0) == container.scrollHeight - container.scrollTop.toFixed(0) ==
container.clientHeight container.clientHeight
@ -560,7 +604,7 @@ export default {
.catch((err) => { .catch((err) => {
console.log("Failed to send quick reaction:", err); console.log("Failed to send quick reaction:", err);
}); });
} },
}, },
}; };
</script> </script>

View file

@ -7,9 +7,12 @@ import matrix from './services/matrix.service'
import 'roboto-fontface/css/roboto/roboto-fontface.css' import 'roboto-fontface/css/roboto/roboto-fontface.css'
import 'material-design-icons-iconfont/dist/material-design-icons.css' import 'material-design-icons-iconfont/dist/material-design-icons.css'
import VEmojiPicker from 'v-emoji-picker'; import VEmojiPicker from 'v-emoji-picker';
import VueResize from 'vue-resize';
import 'vue-resize/dist/vue-resize.css';
Vue.config.productionTip = false Vue.config.productionTip = false
Vue.use(VueResize);
Vue.use(VEmojiPicker); Vue.use(VEmojiPicker);
Vue.use(matrix, {store: store}); Vue.use(matrix, {store: store});