Steven
Steven8 phút đọc

Những cuộc họp bạn thực sự tìm lại được: Bản phát hành Timeline

GeekBye v1.7.3 thay dropdown cuộc họp bằng một timeline thực thụ — ngày giờ kiểu con người, tìm kiếm, một breadcrumb cho biết bạn đang ở cuộc họp nào. Bên dưới: một race condition do auto-select, một screenshot ra đời trước cả cuộc họp của chính nó, và năm bản sửa cho Windows trong vỏn vẹn 28 phút.

Kỹ thuật
UX
Desktop
Bản phát hành GeekBye
Những cuộc họp bạn thực sự tìm lại được: Bản phát hành Timeline

Đến tháng 1 năm 2026, người dùng GeekBye gặp một vấn đề về lịch sử. Ứng dụng ghi và phân tích các cuộc họp của họ hoàn toàn ổn — rồi xếp tất cả vào một cái dropdown. Dropdown ổn với ba mục. Đến cuộc họp thứ mười lăm, việc tìm lại "buổi phỏng vấn thứ Ba tuần trước" trở thành một chuyến khai quật khảo cổ giữa những hàng trông giống hệt nhau. v1.7.3 — 64 commit từ giữa tháng 12 đến ngày 16 tháng 1 — là bản phát hành biến các cuộc họp thành thứ bạn thực sự tìm lại được.

(Một ghi chú sổ sách, bởi lịch sử git thì trung thực ngay cả khi số phiên bản thì không: không hề tồn tại release tag v1.7.2. Phiên bản đã được bump, nhưng tag tiếp theo được xuất xưởng là v1.7.3, nên các bản sửa Windows thuộc thời v1.7.2 đi ké trong bản phát hành này.)

Từ dropdown đến timeline

Thay đổi đinh của bản này nằm gọn trong một pull request: 12 file, +640 dòng, phần lớn là một component MeetingsTimeline mới dài 381 dòng. Dropdown trở thành một timeline cuộn được gồm các thẻ cuộc họp, nhóm vào bốn nhóm — Today, Yesterday, This Week, Older (Hôm nay, Hôm qua, Tuần này, Cũ hơn) — với ngày giờ hiển thị theo cách con người vẫn nói: Today, 2:30 PM, Yesterday, 2:30 PM, Mon, 2:30 PM cho tuần này, và Jan 5, 2:30 PM cho những gì xa hơn. Cmd/Ctrl+K đưa focus vào một ô tìm kiếm lọc theo tiêu đề. Trang chi tiết cuộc họp trở thành một trang điều hướng thực thụ, nên nút back và forward trên navbar rốt cuộc cũng có ý nghĩa, và breadcrumb thôi hiển thị "Meetings / Meeting" mà bắt đầu hiển thị "Meetings / Interview with John" (cắt bớt bằng dấu ba chấm, vì thể nào cũng có người đặt tên cuộc họp dài như tiểu thuyết).

Hai cải thiện nhỏ về trải nghiệm đi kèm: một nút xóa ngay trong trang cuộc họp (xóa xong sẽ đưa bạn về lại timeline, thay vì bỏ mặc bạn trên một trang chết), và tự động điều hướng — khi một phiên nghe kết thúc với nội dung transcript thực sự, dashboard mở thẳng vào trang chi tiết của cuộc họp đó. Kết thúc cuộc gọi, thấy ngay cuộc họp. Những phiên kết thúc rỗng thì được lặng lẽ xóa đi thay vì làm rác timeline.

Cái race condition bạn tự tạo ra khi cải thiện điều hướng

Đây là phần changelog không kể cho bạn. Danh sách cuộc họp cũ có một thói quen: mỗi lần load, nó tự động chọn phiên mới nhất. Vô hại khi danh sách còn là một dropdown bạn hiếm khi động vào. Chí mạng ngay khoảnh khắc cuộc họp trở thành một trang bạn điều hướng tới: click vào cuộc họp #5, một lần refetch chạy nền hoàn tất, và danh sách "nhiệt tình" chọn cuộc họp mới nhất — ghi đè lên lựa chọn bạn vừa đưa ra hai giây trước. Bản sửa không phải là một cái guard khôn khéo hơn; mà là xóa hẳn auto-select. Bất kỳ danh sách nào tự chọn thay người dùng rồi sẽ có ngày đánh nhau với chính người dùng.

Gỡ nó đi làm lộ ra cái race thứ hai. Tự động điều hướng nhảy đến một cuộc họp ngay khoảnh khắc phiên của nó kết thúc — tức có thể là trước khi phiên đó ghi xong vào database SQLite cục bộ. Điều hướng quá nhanh và bạn đến một cuộc họp còn chưa tồn tại. Bản sửa được ship là một khoảng chờ 500ms trước khi load các phiên lúc vào trang chi tiết, và handler điều hướng còn tự thủ thêm mấy lớp phòng hờ của riêng nó — chờ sự kiện did-finish-load của cửa sổ cộng thêm 100ms nếu dashboard vừa được tạo, 50ms nếu nó đã mở sẵn. Cách chúng tôi đọc chuyện này: điều hướng lúc kết thúc phiên băng qua ba miền thời gian — lần ghi database, lần load cửa sổ, và lần mount của React — và mỗi miền được phát cho một khoảng chờ nhỏ của riêng mình. Nó chạy được, và xin nói thẳng: một chồng magic number là món nợ được thừa nhận sòng phẳng, chứ không phải một chiến thắng.

Tấm screenshot ra đời trước cả cuộc họp của chính nó

Thêm một giả định nữa chết trong bản này: "screenshot thuộc về một phiên khi và chỉ khi nó được chụp trong phiên đó." Hoàn toàn logic. Và sai. Người dùng thật bấm Cmd+H lên tin tuyển dụng, lên agenda, lên đoạn code họ sắp thảo luận — trước khi bấm nút Start (bắt đầu). Tab Report so khớp screenshot với khoảng thời gian của phiên một cách chặt chẽ, nên những screenshot chuẩn bị đó biến mất khỏi chính bản báo cáo chúng được chụp cho. Bản sửa nới cửa sổ so khớp thêm 60 giây trước thời điểm phiên bắt đầu. (Bản thân bộ máy time-range thì cũ hơn — nó đã lặng lẽ ship trong v1.6.15; đóng góp của v1.7.3 là dạy nó rằng cuộc họp bắt đầu sớm hơn một chút so với lúc nó bắt đầu.)

Năm bản sửa trong 28 phút

Chuyện chiến trường của bản phát hành này lại chẳng nằm trong timeline — nó nằm ở overlay onboarding trên Windows, và là một chuỗi "sửa cái bản sửa" kinh điển: năm commit từ 10:50 đến 11:18 trong cùng một buổi sáng.

Cửa sổ chính của GeekBye dùng mouse passthrough — một hook bật tắt setIgnoreMouseEvents của Electron trên mỗi lần chuột di chuyển, để click rơi xuyên qua các vùng trong suốt. Trong lúc onboarding trên Windows, hook đó đánh nhau với overlay hướng dẫn, và đến bước cửa sổ dashboard xuất hiện, ứng dụng đứng hình. Bản sửa thứ nhất: cho hook không làm gì cả khi onboarding đang chạy. Hòa bình bằng cách buông vũ khí.

Có điều bây giờ các nút onboarding không phản hồi nữa, vì event forwarding đang chuyển các cú click xuống cửa sổ dashboard nằm bên dưới overlay. Bản sửa thứ hai và thứ ba loay hoay chỉnh forward: false cho từng wizard. Rồi đến cái commit tìm ra nguyên nhân gốc thực sự: khi cửa sổ dashboard mở, drag handler của nó reset cửa sổ chính về chế độ click-through — mà bản sửa thứ nhất đã biến hook passthrough thành thụ động, nghĩa là chẳng còn gì để khắc phục sự can thiệp đó nữa. Bản sửa thật đảo ngược chiến lược: giờ đây hook chủ động khẳng định lại setIgnoreMouseEvents(false) trên mỗi lần chuột di chuyển trong lúc onboarding, chính vì code bên ngoài có thể reset nó bất cứ lúc nào. Nó cũng xóa luôn 75 dòng workaround theo-từng-wizard trước đó.

Và kể cả vậy vẫn còn một commit thứ năm, vì thắng cuộc chiến input không có nghĩa là thắng cuộc chiến z-order: overlay và dashboard cùng ngồi ở một mức always-on-top, và Windows đẩy lên trên bất kỳ cửa sổ nào nhận được focus. Overlay chuyển lên một mức cửa sổ cao hơn và tự nâng mình lên lại sau một khoảng trễ khi dashboard xuất hiện. Cái vòng cung này đáng ghi nhớ: một bản sửa thụ động tước vũ khí của người phòng thủ duy nhất còn tệ hơn cả cuộc chiến ban đầu.

Ba điều bản phát hành này dạy chúng tôi

  1. Nâng cấp điều hướng làm lộ bug trạng thái. Cái race của auto-select đã tồn tại từ trước v1.7.3; một dropdown chỉ đơn giản là không bao giờ để bạn cảm nhận nó. Khoảnh khắc cuộc họp trở thành một trang bạn chủ động điều hướng tới, mọi đoạn code chọn thứ gì đó thay bạn đều trở thành bug.
  2. Ranh giới thời gian quanh ý định người dùng cần có độ nới. Phiên, bản ghi, báo cáo — người dùng hành động sớm hơn và muộn hơn một chút so với cửa sổ chính thức của máy. Bộ đệm screenshot 60 giây là một dòng code phản ánh đúng cách con người thực sự hành xử.
  3. Đừng bao giờ để code sửa sai của bạn thành thụ động. Vụ đứng hình trên Windows tốn năm bản sửa vì bản đầu tiên tắt đi cơ chế duy nhất có thể kháng lại sự can thiệp từ bên ngoài. Nếu code khác có thể phá invariant của bạn bất cứ lúc nào, việc của bạn là liên tục khẳng định lại nó, chứ không phải rút lui.

Về chương trước trong câu chuyện v1 — build hai ứng dụng mang thương hiệu riêng từ chính codebase này — xem một codebase, hai ứng dụng: cách white-label mà không cần fork (v1.7.1); và cho toàn bộ vòng cung, xem giải phẫu việc xuất xưởng phần mềm đến độ hoàn hảo.

Bài Viết Liên Quan