From 1c7a83f56a041fefc0aec9dc509bd3155c1988ac Mon Sep 17 00:00:00 2001 From: David Laban Date: Mon, 23 Dec 2024 16:29:22 +0000 Subject: [PATCH 01/10] test: playwright project for .mobile.spec.ts files --- tests/playwright.config.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/playwright.config.ts b/tests/playwright.config.ts index 728b25ad5bba..69280d5c841f 100644 --- a/tests/playwright.config.ts +++ b/tests/playwright.config.ts @@ -1,6 +1,6 @@ import type { PlaywrightWorkerOptions } from '@playwright/test'; -import { defineConfig } from '@playwright/test'; +import { defineConfig, devices } from '@playwright/test'; import process from 'node:process'; export default defineConfig({ @@ -17,6 +17,27 @@ export default defineConfig({ COVERAGE: process.env.COVERAGE ?? '', }, }, + projects: [ + // To avoid burning github actions minutes, we only run interesting tests on mobile. + // The rest of the tests are run on desktop. + // .spec.ts files are run on desktop only + // .mobile.spec.ts files are run on mobile only + // .shared.spec.ts files are run on both desktop and mobile + { + name: 'desktop', + testIgnore: 'tests/**/*.mobile.spec.ts', + }, + { + name: 'mobile', + testMatch: [ + 'tests/**/*.mobile.spec.ts', + 'tests/**/*.shared.spec.ts', + ], + // Note: we ignore the BROWSER environment variable here. + // We can special-case firefox/webkit in the future if this causes us to miss bugs in CI. + use: { ...devices['Pixel 7'] }, + }, + ], use: { browserName: (process.env.BROWSER as PlaywrightWorkerOptions['browserName']) ?? From 8b46e4baf39a5bbd54fbae1d9aead8801b37af4f Mon Sep 17 00:00:00 2001 From: David Laban Date: Mon, 23 Dec 2024 16:56:15 +0000 Subject: [PATCH 02/10] test: simple shared rendering test for mobile and desktop kanban --- tests/database/kanban.shared.spec.ts | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 tests/database/kanban.shared.spec.ts diff --git a/tests/database/kanban.shared.spec.ts b/tests/database/kanban.shared.spec.ts new file mode 100644 index 000000000000..0e0dc7b10239 --- /dev/null +++ b/tests/database/kanban.shared.spec.ts @@ -0,0 +1,31 @@ + +import { + enterPlaygroundRoom, + initKanbanViewState, +} from '../utils/actions/index.js'; +import { test } from '../utils/playwright.js'; +import { + focusKanbanCardHeader, +} from './actions.js'; + +test.describe('kanban view', () => { + test('basic rendering of kanban card', async ({ page }) => { + await enterPlaygroundRoom(page); + await initKanbanViewState(page, { + rows: ['row1'], + columns: [ + { + type: 'number', + value: [1], + }, + { + type: 'rich-text', + value: ['text'], + }, + ], + }); + + await focusKanbanCardHeader(page); + }); + +}); From c4c9eb0ae1eaa48a80d2e3cab00e17040f11a657 Mon Sep 17 00:00:00 2001 From: David Laban Date: Mon, 23 Dec 2024 18:10:49 +0000 Subject: [PATCH 03/10] test: drag and drop test that works runs on desktop on mobile projects --- tests/database/kanban.shared.spec.ts | 18 +++++++++++++++++- tests/utils/query.ts | 25 ++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/tests/database/kanban.shared.spec.ts b/tests/database/kanban.shared.spec.ts index 0e0dc7b10239..df0435514024 100644 --- a/tests/database/kanban.shared.spec.ts +++ b/tests/database/kanban.shared.spec.ts @@ -1,4 +1,6 @@ +import { portableLocator } from 'utils/query.js'; + import { enterPlaygroundRoom, initKanbanViewState, @@ -9,7 +11,7 @@ import { } from './actions.js'; test.describe('kanban view', () => { - test('basic rendering of kanban card', async ({ page }) => { + test('drag and drop', async ({ page }) => { await enterPlaygroundRoom(page); await initKanbanViewState(page, { rows: ['row1'], @@ -26,6 +28,20 @@ test.describe('kanban view', () => { }); await focusKanbanCardHeader(page); + // https://playwright.dev/docs/input#dragging-manually mentions that you may need two drags to + // trigger `dragover`, so we drag to our own column header before dragging to "Ungroups". + await portableLocator(page, 'affine-data-view-kanban-card').hover() + await page.mouse.down(); + await page.locator('[data-wc-dnd-drag-handler-id="g:0"]').hover(); + await page.locator('[data-wc-dnd-drag-handler-id="Ungroups"]').hover({ force: true }); + await page.mouse.up(); + + if (test.info().project.name === 'mobile') { + // Mobile does not support drag and drop yet + test.fixme(); + } + // When we drag into "Ungroups", our old group collapses. + await test.expect(page.locator('[data-wc-dnd-drag-handler-id="g:0"]')).not.toBeVisible(); }); }); diff --git a/tests/utils/query.ts b/tests/utils/query.ts index d8c40d15af3e..dfcc12c51c69 100644 --- a/tests/utils/query.ts +++ b/tests/utils/query.ts @@ -1,4 +1,4 @@ -import { expect, type Page } from '@playwright/test'; +import { test, expect, type Page } from '@playwright/test'; import { waitNextFrame } from './actions/misc.js'; import { assertAlmostEqual } from './asserts.js'; @@ -123,3 +123,26 @@ export function getEmbedCardToolbar(page: Page) { cardStyleListButton, }; } + +/** + * mobile views typically use different component names. + * + * If you want to write a test that works on both desktop and mobile, + * you need to use a different component name on mobile. + * + * Typically, it is good enough to replace the `affine-data-view-` + * prefix and replace it with `mobile-`. + * + * In the future, we might want to consider using data-testid attributes instead. + */ +export function portableLocator(page: Page, selector: string) { + if (!selector.startsWith('affine-data-view-')) { + throw new Error(`Received invalid selector '${selector}'). Please use a selector starting with affine-data-view-`); + } + + if (test.info().project.name === 'mobile') { + return page.locator(selector.replaceAll('affine-data-view-', 'mobile-')); + } + + return page.locator(selector); +} From c586d6ebe1f9a27fc799303f55ac0def555844d5 Mon Sep 17 00:00:00 2001 From: David Laban Date: Tue, 3 Dec 2024 18:06:33 +0000 Subject: [PATCH 04/10] feat(database): copy-paste kanban drag controller for mobile --- .../kanban/mobile/controller/drag.ts | 246 ++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 packages/affine/data-view/src/view-presets/kanban/mobile/controller/drag.ts diff --git a/packages/affine/data-view/src/view-presets/kanban/mobile/controller/drag.ts b/packages/affine/data-view/src/view-presets/kanban/mobile/controller/drag.ts new file mode 100644 index 000000000000..ee9c54d69c1c --- /dev/null +++ b/packages/affine/data-view/src/view-presets/kanban/mobile/controller/drag.ts @@ -0,0 +1,246 @@ +import type { InsertToPosition } from '@blocksuite/affine-shared/utils'; +import type { ReactiveController } from 'lit'; + +import { assertExists, Point, Rect } from '@blocksuite/global/utils'; +import { computed } from '@preact/signals-core'; + +import type { DataViewKanban } from '../kanban-view.js'; + +import { autoScrollOnBoundary } from '../../../../core/utils/auto-scroll.js'; +import { startDrag } from '../../../../core/utils/drag.js'; +import { KanbanCard } from '../card.js'; +import { KanbanGroup } from '../group.js'; + +export class KanbanDragController implements ReactiveController { + dragStart = (ele: KanbanCard, evt: PointerEvent) => { + const eleRect = ele.getBoundingClientRect(); + const offsetLeft = evt.x - eleRect.left; + const offsetTop = evt.y - eleRect.top; + const preview = createDragPreview( + ele, + evt.x - offsetLeft, + evt.y - offsetTop + ); + const currentGroup = ele.closest('affine-data-view-kanban-group'); + const drag = startDrag< + | { type: 'out'; callback: () => void } + | { + type: 'self'; + key: string; + position: InsertToPosition; + } + | undefined, + PointerEvent + >(evt, { + onDrag: () => undefined, + onMove: evt => { + if (!(evt.target instanceof HTMLElement)) { + return; + } + preview.display(evt.x - offsetLeft, evt.y - offsetTop); + if (!Rect.fromDOM(this.host).isPointIn(Point.from(evt))) { + const callback = this.host.props.onDrag; + if (callback) { + this.dropPreview.remove(); + return { + type: 'out', + callback: callback(evt, ele.cardId), + }; + } + return; + } + const result = this.shooIndicator(evt, ele); + if (result) { + return { + type: 'self', + key: result.group.group.key, + position: result.position, + }; + } + return; + }, + onClear: () => { + preview.remove(); + this.dropPreview.remove(); + cancelScroll(); + }, + onDrop: result => { + if (!result) { + return; + } + if (result.type === 'out') { + result.callback(); + return; + } + if (result && currentGroup) { + currentGroup.group.manager.moveCardTo( + ele.cardId, + currentGroup.group.key, + result.key, + result.position + ); + } + }, + }); + const cancelScroll = autoScrollOnBoundary( + this.scrollContainer, + computed(() => { + return { + left: drag.mousePosition.value.x, + right: drag.mousePosition.value.x, + top: drag.mousePosition.value.y, + bottom: drag.mousePosition.value.y, + }; + }) + ); + }; + + dropPreview = createDropPreview(); + + getInsertPosition = ( + evt: MouseEvent + ): + | { group: KanbanGroup; card?: KanbanCard; position: InsertToPosition } + | undefined => { + const eles = document.elementsFromPoint(evt.x, evt.y); + const target = eles.find(v => v instanceof KanbanGroup) as KanbanGroup; + if (target) { + const card = getCardByPoint(target, evt.y); + return { + group: target, + card, + position: card + ? { + before: true, + id: card.cardId, + } + : 'end', + }; + } else { + return; + } + }; + + shooIndicator = ( + evt: MouseEvent, + self: KanbanCard | undefined + ): { group: KanbanGroup; position: InsertToPosition } | undefined => { + const position = this.getInsertPosition(evt); + if (position) { + this.dropPreview.display(position.group, self, position.card); + } else { + this.dropPreview.remove(); + } + return position; + }; + + get scrollContainer() { + const scrollContainer = this.host.querySelector( + '.affine-data-view-kanban-groups' + ) as HTMLElement; + assertExists(scrollContainer); + return scrollContainer; + } + + constructor(private host: DataViewKanban) { + this.host.addController(this); + } + + hostConnected() { + if (this.host.props.view.readonly$.value) { + return; + } + this.host.disposables.add( + this.host.props.handleEvent('dragStart', context => { + const event = context.get('pointerState').raw; + const target = event.target; + if (target instanceof Element) { + const cell = target.closest('affine-data-view-kanban-cell'); + if (cell?.editing) { + return; + } + cell?.selectCurrentCell(false); + const card = target.closest('affine-data-view-kanban-card'); + if (card) { + this.dragStart(card, event); + } + } + return true; + }) + ); + } +} + +const createDragPreview = (card: KanbanCard, x: number, y: number) => { + const preOpacity = card.style.opacity; + card.style.opacity = '0.5'; + const div = document.createElement('div'); + const kanbanCard = new KanbanCard(); + kanbanCard.cardId = card.cardId; + kanbanCard.view = card.view; + kanbanCard.isFocus = true; + kanbanCard.style.backgroundColor = 'var(--affine-background-primary-color)'; + div.append(kanbanCard); + div.className = 'with-data-view-css-variable'; + div.style.width = `${card.getBoundingClientRect().width}px`; + div.style.position = 'fixed'; + // div.style.pointerEvents = 'none'; + div.style.transform = 'rotate(-3deg)'; + div.style.left = `${x}px`; + div.style.top = `${y}px`; + div.style.zIndex = '9999'; + document.body.append(div); + return { + display(x: number, y: number) { + div.style.left = `${Math.round(x)}px`; + div.style.top = `${Math.round(y)}px`; + }, + remove() { + card.style.opacity = preOpacity; + div.remove(); + }, + }; +}; +const createDropPreview = () => { + const div = document.createElement('div'); + div.style.height = '2px'; + div.style.borderRadius = '1px'; + div.style.backgroundColor = 'var(--affine-primary-color)'; + div.style.boxShadow = '0px 0px 8px 0px rgba(30, 150, 235, 0.35)'; + return { + display( + group: KanbanGroup, + self: KanbanCard | undefined, + card?: KanbanCard + ) { + const target = card ?? group.querySelector('.add-card'); + assertExists(target); + if (target.previousElementSibling === self || target === self) { + div.remove(); + return; + } + if (target.previousElementSibling === div) { + return; + } + target.insertAdjacentElement('beforebegin', div); + }, + remove() { + div.remove(); + }, + }; +}; + +const getCardByPoint = ( + group: KanbanGroup, + y: number +): KanbanCard | undefined => { + const cards = Array.from( + group.querySelectorAll('affine-data-view-kanban-card') + ); + const positions = cards.map(v => { + const rect = v.getBoundingClientRect(); + return (rect.top + rect.bottom) / 2; + }); + const index = positions.findIndex(v => v > y); + return cards[index]; +}; From 55d75a600be8383e9e4d004603b7a69cb2d5b05a Mon Sep 17 00:00:00 2001 From: David Laban Date: Tue, 3 Dec 2024 18:24:22 +0000 Subject: [PATCH 05/10] feat(database): plug in MobileKanbanDragController and fix errors but don't do anything with it --- .../kanban/mobile/controller/drag.ts | 46 +++++++++---------- .../view-presets/kanban/mobile/kanban-view.ts | 3 ++ 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/packages/affine/data-view/src/view-presets/kanban/mobile/controller/drag.ts b/packages/affine/data-view/src/view-presets/kanban/mobile/controller/drag.ts index ee9c54d69c1c..42fa860b5f30 100644 --- a/packages/affine/data-view/src/view-presets/kanban/mobile/controller/drag.ts +++ b/packages/affine/data-view/src/view-presets/kanban/mobile/controller/drag.ts @@ -4,15 +4,15 @@ import type { ReactiveController } from 'lit'; import { assertExists, Point, Rect } from '@blocksuite/global/utils'; import { computed } from '@preact/signals-core'; -import type { DataViewKanban } from '../kanban-view.js'; +import type { MobileDataViewKanban } from '../kanban-view.js'; import { autoScrollOnBoundary } from '../../../../core/utils/auto-scroll.js'; import { startDrag } from '../../../../core/utils/drag.js'; -import { KanbanCard } from '../card.js'; -import { KanbanGroup } from '../group.js'; +import { MobileKanbanCard } from '../card.js'; +import { MobileKanbanGroup } from '../group.js'; -export class KanbanDragController implements ReactiveController { - dragStart = (ele: KanbanCard, evt: PointerEvent) => { +export class MobileKanbanDragController implements ReactiveController { + dragStart = (ele: MobileKanbanCard, evt: PointerEvent) => { const eleRect = ele.getBoundingClientRect(); const offsetLeft = evt.x - eleRect.left; const offsetTop = evt.y - eleRect.top; @@ -21,7 +21,7 @@ export class KanbanDragController implements ReactiveController { evt.x - offsetLeft, evt.y - offsetTop ); - const currentGroup = ele.closest('affine-data-view-kanban-group'); + const currentGroup = ele.closest('mobile-kanban-group'); const drag = startDrag< | { type: 'out'; callback: () => void } | { @@ -100,10 +100,10 @@ export class KanbanDragController implements ReactiveController { getInsertPosition = ( evt: MouseEvent ): - | { group: KanbanGroup; card?: KanbanCard; position: InsertToPosition } + | { group: MobileKanbanGroup; card?: MobileKanbanCard; position: InsertToPosition } | undefined => { const eles = document.elementsFromPoint(evt.x, evt.y); - const target = eles.find(v => v instanceof KanbanGroup) as KanbanGroup; + const target = eles.find(v => v instanceof MobileKanbanGroup) as MobileKanbanGroup; if (target) { const card = getCardByPoint(target, evt.y); return { @@ -123,8 +123,8 @@ export class KanbanDragController implements ReactiveController { shooIndicator = ( evt: MouseEvent, - self: KanbanCard | undefined - ): { group: KanbanGroup; position: InsertToPosition } | undefined => { + self: MobileKanbanCard | undefined + ): { group: MobileKanbanGroup; position: InsertToPosition } | undefined => { const position = this.getInsertPosition(evt); if (position) { this.dropPreview.display(position.group, self, position.card); @@ -136,13 +136,13 @@ export class KanbanDragController implements ReactiveController { get scrollContainer() { const scrollContainer = this.host.querySelector( - '.affine-data-view-kanban-groups' + '.mobile-kanban-groups' ) as HTMLElement; assertExists(scrollContainer); return scrollContainer; } - constructor(private host: DataViewKanban) { + constructor(private host: MobileDataViewKanban) { this.host.addController(this); } @@ -155,12 +155,12 @@ export class KanbanDragController implements ReactiveController { const event = context.get('pointerState').raw; const target = event.target; if (target instanceof Element) { - const cell = target.closest('affine-data-view-kanban-cell'); - if (cell?.editing) { + const cell = target.closest('mobile-kanban-cell'); + if (cell?.isEditing) { return; } cell?.selectCurrentCell(false); - const card = target.closest('affine-data-view-kanban-card'); + const card = target.closest('mobile-kanban-card'); if (card) { this.dragStart(card, event); } @@ -171,11 +171,11 @@ export class KanbanDragController implements ReactiveController { } } -const createDragPreview = (card: KanbanCard, x: number, y: number) => { +const createDragPreview = (card: MobileKanbanCard, x: number, y: number) => { const preOpacity = card.style.opacity; card.style.opacity = '0.5'; const div = document.createElement('div'); - const kanbanCard = new KanbanCard(); + const kanbanCard = new MobileKanbanCard(); kanbanCard.cardId = card.cardId; kanbanCard.view = card.view; kanbanCard.isFocus = true; @@ -209,9 +209,9 @@ const createDropPreview = () => { div.style.boxShadow = '0px 0px 8px 0px rgba(30, 150, 235, 0.35)'; return { display( - group: KanbanGroup, - self: KanbanCard | undefined, - card?: KanbanCard + group: MobileKanbanGroup, + self: MobileKanbanCard | undefined, + card?: MobileKanbanCard ) { const target = card ?? group.querySelector('.add-card'); assertExists(target); @@ -231,11 +231,11 @@ const createDropPreview = () => { }; const getCardByPoint = ( - group: KanbanGroup, + group: MobileKanbanGroup, y: number -): KanbanCard | undefined => { +): MobileKanbanCard | undefined => { const cards = Array.from( - group.querySelectorAll('affine-data-view-kanban-card') + group.querySelectorAll('mobile-kanban-card') ); const positions = cards.map(v => { const rect = v.getBoundingClientRect(); diff --git a/packages/affine/data-view/src/view-presets/kanban/mobile/kanban-view.ts b/packages/affine/data-view/src/view-presets/kanban/mobile/kanban-view.ts index 767163663fe8..d53cfdd63460 100644 --- a/packages/affine/data-view/src/view-presets/kanban/mobile/kanban-view.ts +++ b/packages/affine/data-view/src/view-presets/kanban/mobile/kanban-view.ts @@ -16,6 +16,7 @@ import type { KanbanViewSelectionWithType } from '../types.js'; import { type DataViewInstance, renderUniLit } from '../../../core/index.js'; import { sortable } from '../../../core/utils/wc-dnd/sort/sort-context.js'; import { DataViewBase } from '../../../core/view/data-view-base.js'; +import { MobileKanbanDragController } from './controller/drag.js'; const styles = css` mobile-data-view-kanban { @@ -52,6 +53,8 @@ export class MobileDataViewKanban extends DataViewBase< > { static override styles = styles; + private dragController = new MobileKanbanDragController(this); + renderAddGroup = () => { const addGroup = this.groupManager.addGroup; if (!addGroup) { From a13967f8b31ac3632d4a66564e78180e007ca471 Mon Sep 17 00:00:00 2001 From: David Laban Date: Tue, 24 Dec 2024 07:28:44 +0000 Subject: [PATCH 06/10] test: enable mobile drag test --- tests/database/kanban.shared.spec.ts | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/tests/database/kanban.shared.spec.ts b/tests/database/kanban.shared.spec.ts index df0435514024..59cf4bd22d58 100644 --- a/tests/database/kanban.shared.spec.ts +++ b/tests/database/kanban.shared.spec.ts @@ -1,4 +1,3 @@ - import { portableLocator } from 'utils/query.js'; import { @@ -6,9 +5,7 @@ import { initKanbanViewState, } from '../utils/actions/index.js'; import { test } from '../utils/playwright.js'; -import { - focusKanbanCardHeader, -} from './actions.js'; +import { focusKanbanCardHeader } from './actions.js'; test.describe('kanban view', () => { test('drag and drop', async ({ page }) => { @@ -30,18 +27,17 @@ test.describe('kanban view', () => { await focusKanbanCardHeader(page); // https://playwright.dev/docs/input#dragging-manually mentions that you may need two drags to // trigger `dragover`, so we drag to our own column header before dragging to "Ungroups". - await portableLocator(page, 'affine-data-view-kanban-card').hover() + await portableLocator(page, 'affine-data-view-kanban-card').hover(); await page.mouse.down(); await page.locator('[data-wc-dnd-drag-handler-id="g:0"]').hover(); - await page.locator('[data-wc-dnd-drag-handler-id="Ungroups"]').hover({ force: true }); + await page + .locator('[data-wc-dnd-drag-handler-id="Ungroups"]') + .hover({ force: true }); await page.mouse.up(); - if (test.info().project.name === 'mobile') { - // Mobile does not support drag and drop yet - test.fixme(); - } // When we drag into "Ungroups", our old group collapses. - await test.expect(page.locator('[data-wc-dnd-drag-handler-id="g:0"]')).not.toBeVisible(); + await test + .expect(page.locator('[data-wc-dnd-drag-handler-id="g:0"]')) + .not.toBeVisible(); }); - }); From 36dee37be0993e5c9a5ef71d90a33aa5222a464b Mon Sep 17 00:00:00 2001 From: David Laban Date: Fri, 27 Dec 2024 16:15:47 +0000 Subject: [PATCH 07/10] fix(database): handle kanban card dragging ourselves If you switch to desktop mode, add a kanban board, then switch to touch emulation mode, the pc kanban board now correctly responds to touch-based drag events. Now I just need to wire up the mobile component so that it looks like the pc one. --- packages/affine/data-view/src/view-presets/kanban/mobile/card.ts | 1 + packages/affine/data-view/src/view-presets/kanban/pc/card.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/affine/data-view/src/view-presets/kanban/mobile/card.ts b/packages/affine/data-view/src/view-presets/kanban/mobile/card.ts index a56068436577..458b580ce0ec 100644 --- a/packages/affine/data-view/src/view-presets/kanban/mobile/card.ts +++ b/packages/affine/data-view/src/view-presets/kanban/mobile/card.ts @@ -23,6 +23,7 @@ const styles = css` box-shadow: 0px 2px 3px 0px rgba(0, 0, 0, 0.05); border-radius: 8px; background-color: var(--affine-background-kanban-card-color); + touch-action: none; } .mobile-card-header { diff --git a/packages/affine/data-view/src/view-presets/kanban/pc/card.ts b/packages/affine/data-view/src/view-presets/kanban/pc/card.ts index a7b8a6c8743f..f6fb6e9ce369 100644 --- a/packages/affine/data-view/src/view-presets/kanban/pc/card.ts +++ b/packages/affine/data-view/src/view-presets/kanban/pc/card.ts @@ -23,6 +23,7 @@ const styles = css` border-radius: 8px; transition: background-color 100ms ease-in-out; background-color: var(--affine-background-kanban-card-color); + touch-action: none; } affine-data-view-kanban-card:hover { From 529b2f3301c249095b535a6fcc9274bbc6d489b9 Mon Sep 17 00:00:00 2001 From: David Laban Date: Fri, 27 Dec 2024 16:20:32 +0000 Subject: [PATCH 08/10] fix(database): don't get in bad state when browser cancels drag --- packages/affine/data-view/src/core/utils/drag.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/affine/data-view/src/core/utils/drag.ts b/packages/affine/data-view/src/core/utils/drag.ts index b5443c1aaf2e..9afd49779572 100644 --- a/packages/affine/data-view/src/core/utils/drag.ts +++ b/packages/affine/data-view/src/core/utils/drag.ts @@ -35,6 +35,7 @@ export const startDrag = < const clear = () => { window.removeEventListener('pointermove', move); window.removeEventListener('pointerup', up); + window.removeEventListener('pointercancel', up); window.removeEventListener('keydown', keydown); document.body.style.cursor = oldCursor; ops.onClear(); @@ -63,6 +64,7 @@ export const startDrag = < }; window.addEventListener('pointermove', move); window.addEventListener('pointerup', up); + window.addEventListener('pointercancel', up); window.addEventListener('keydown', keydown); return result; From 3afc01db0fbefe0b07873d0c08bf46802b666dea Mon Sep 17 00:00:00 2001 From: David Laban Date: Fri, 27 Dec 2024 18:19:28 +0000 Subject: [PATCH 09/10] REVERTME: make mobile db views read-write --- packages/blocks/src/database-block/data-source.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/blocks/src/database-block/data-source.ts b/packages/blocks/src/database-block/data-source.ts index eff75599eccf..5f0f1555344f 100644 --- a/packages/blocks/src/database-block/data-source.ts +++ b/packages/blocks/src/database-block/data-source.ts @@ -17,7 +17,6 @@ import { type ViewMeta, } from '@blocksuite/data-view'; import { propertyPresets } from '@blocksuite/data-view/property-presets'; -import { IS_MOBILE } from '@blocksuite/global/env'; import { assertExists } from '@blocksuite/global/utils'; import { type BlockModel, nanoid, Text } from '@blocksuite/store'; import { computed, type ReadonlySignal } from '@preact/signals-core'; @@ -71,9 +70,8 @@ export class DatabaseBlockDataSource extends DataSourceBase { readonly$: ReadonlySignal = computed(() => { return ( - this._model.doc.readonly || - // TODO(@L-Sun): use block level readonly - IS_MOBILE + // FIXME: make this readonly for most mobile blocks but readwrite for kanban? + this._model.doc.readonly ); }); From 4eab55bd67f182a654566aaf4e525232b661d914 Mon Sep 17 00:00:00 2001 From: David Laban Date: Fri, 27 Dec 2024 18:22:07 +0000 Subject: [PATCH 10/10] fix(database): make mobile kanban-view more similar to pc version this was just from looking at 'code --diff */kanban-view.ts'. It might be pointless cargo-culting --- .../data-view/src/view-presets/kanban/mobile/kanban-view.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/affine/data-view/src/view-presets/kanban/mobile/kanban-view.ts b/packages/affine/data-view/src/view-presets/kanban/mobile/kanban-view.ts index d53cfdd63460..42ed93e3136d 100644 --- a/packages/affine/data-view/src/view-presets/kanban/mobile/kanban-view.ts +++ b/packages/affine/data-view/src/view-presets/kanban/mobile/kanban-view.ts @@ -98,8 +98,8 @@ export class MobileDataViewKanban extends DataViewBase< }, hideIndicator: () => {}, moveTo: () => {}, - showIndicator: () => { - return false; + showIndicator: evt => { + return this.dragController.shooIndicator(evt, undefined) != null; }, view: this.props.view, eventTrace: this.props.eventTrace,