[fix] Allow interactions with embeds in readonly mode (#1333)

Allow the users to interact with embeds in readonly mode. Not sure if
this should apply to all embeds (cc @orangemug for thoughts on this)?
For example, you can also start editing code sandbox embeds.

One thing that doesn't feel quite right is that readonly mode defaults
to the hand tool, so you always have to switch to select tool to get
this working. I guess moving around is still the more common action
though so 🤷

### Test Plan

1. Create a multiplayer room with some embeds (youtube videos, spotify
playlist).
2. Open the room in readonly mode.
3. Make sure you can interact with embeds. Double click / enter when
selected, you should then be able to play the youtube videos.

- [x] Unit Tests
- [ ] Webdriver tests

### Release Note

- Allow the users to interact with embeds in readonly mode.
This commit is contained in:
Mitja Bezenšek 2023-05-15 17:37:54 +02:00 committed by GitHub
parent a824168e0a
commit 2c8b431c1f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 95 additions and 28 deletions

View file

@ -43,7 +43,7 @@ export class Idle extends StateNode {
onPointerDown: TLEventHandlers['onPointerDown'] = (info) => {
if (this.app.isMenuOpen) return
const shouldEnterCropMode = this.shouldEnterCropMode(info)
const shouldEnterCropMode = this.shouldEnterCropMode(info, true)
if (info.ctrlKey && !shouldEnterCropMode) {
this.parent.transition('brushing', info)
@ -157,8 +157,8 @@ export class Idle extends StateNode {
const { shape } = info
const util = this.app.getShapeUtil(shape)
// Allow video playing
if (shape.type !== 'video' && this.app.isReadOnly) break
// Allow playing videos and embeds
if (shape.type !== 'video' && shape.type !== 'embed' && this.app.isReadOnly) break
if (util.onDoubleClick) {
// Call the shape's double click handler
@ -268,43 +268,72 @@ export class Idle extends StateNode {
}
onKeyUp = (info: TLKeyboardEventInfo) => {
if (this.app.isReadOnly) return
switch (info.code) {
case 'Enter': {
const { selectedShapes } = this.app
if (selectedShapes.every((shape) => shape.type === 'group')) {
this.app.setSelectedIds(
selectedShapes.flatMap((shape) => this.app.getSortedChildIds(shape.id))
)
return
if (this.app.isReadOnly) {
switch (info.code) {
case 'Enter': {
if (this.shouldStartEditingShape() && this.app.onlySelectedShape) {
this.startEditingShape(this.app.onlySelectedShape, {
...info,
target: 'shape',
shape: this.app.onlySelectedShape,
})
return
}
break
}
}
} else {
switch (info.code) {
case 'Enter': {
const { selectedShapes } = this.app
const { onlySelectedShape } = this.app
const util = onlySelectedShape && this.app.getShapeUtil(onlySelectedShape)
if (util && util.canEdit(onlySelectedShape)) {
this.startEditingShape(onlySelectedShape, {
...info,
target: 'shape',
shape: onlySelectedShape,
})
return
}
if (selectedShapes.every((shape) => shape.type === 'group')) {
this.app.setSelectedIds(
selectedShapes.flatMap((shape) => this.app.getSortedChildIds(shape.id))
)
return
}
if (util && util.canCrop(onlySelectedShape)) {
this.parent.transition('crop', info)
if (this.shouldStartEditingShape() && this.app.onlySelectedShape) {
this.startEditingShape(this.app.onlySelectedShape, {
...info,
target: 'shape',
shape: this.app.onlySelectedShape,
})
return
}
if (this.shouldEnterCropMode(info, false)) {
this.parent.transition('crop', info)
}
break
}
break
}
}
}
private shouldEnterCropMode(info: TLPointerEventInfo): boolean {
private shouldStartEditingShape(): boolean {
const { onlySelectedShape } = this.app
if (!onlySelectedShape) return false
const util = this.app.getShapeUtil(onlySelectedShape)
return util.canEdit(onlySelectedShape)
}
private shouldEnterCropMode(
info: TLPointerEventInfo | TLKeyboardEventInfo,
withCtrlKey: boolean
): boolean {
const singleShape = this.app.onlySelectedShape
if (!singleShape) return false
const shapeUtil = this.app.getShapeUtil(singleShape)
return shapeUtil.canCrop(singleShape) && info.ctrlKey
// Should the Ctrl key be pressed to enter crop mode
if (withCtrlKey) {
return shapeUtil.canCrop(singleShape) && info.ctrlKey
} else {
return shapeUtil.canCrop(singleShape)
}
}
private startEditingShape(shape: TLShape, info: TLClickEventInfo | TLKeyboardEventInfo) {

View file

@ -5,6 +5,7 @@ let app: TestApp
const ids = {
box1: createCustomShapeId('box1'),
embed1: createCustomShapeId('embed1'),
}
jest.useFakeTimers()
@ -350,3 +351,40 @@ describe('When editing shapes', () => {
it.todo('restores selection after changing styles')
})
describe('When in readonly mode', () => {
beforeEach(() => {
app.createShapes([
{
id: ids.embed1,
type: 'embed',
x: 100,
y: 100,
props: { opacity: '1', w: 100, h: 100, url: '', doesResize: false },
},
])
app.updateUserDocumentSettings({ isReadOnly: true })
})
it('Begins editing embed when double clicked', () => {
expect(app.editingId).toBe(null)
expect(app.selectedIds.length).toBe(0)
expect(app.isReadOnly).toBe(true)
const shape = app.getShapeById(ids.embed1)
app.doubleClick(100, 100, { target: 'shape', shape })
expect(app.editingId).toBe(ids.embed1)
})
it('Begins editing embed when pressing Enter on a selected embed', () => {
expect(app.editingId).toBe(null)
expect(app.selectedIds.length).toBe(0)
expect(app.isReadOnly).toBe(true)
app.setSelectedIds([ids.embed1])
expect(app.selectedIds.length).toBe(1)
app.keyUp('Enter')
expect(app.editingId).toBe(ids.embed1)
})
})