add-in: auto-open taskpane phase 1 + final save after versioning + tracking id = fid
- manifest: TaskpaneId -> Office.AutoShowTaskpaneWithDocument (auto-open marker), bump 1.0.0.4 - metadataService.enableAutoOpenTaskpane: tag doc setting (idempotent, best-effort) called from detect() after sign-in - versionFlow: final saveDocumentVerified at end of both versioning flows (bug: stamped docx never persisted -> reopen showed untracked) - tracking id now uses ticket fid (TRACKING_ID_SOURCE="fid", rollback by switching to "guid"), resolveTrackingId + authService.getCurrentFid - WordAddinGate: restore history.pushState/replaceState that office.js nullifies in taskpane (fixes Next App Router crash) Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
02894f2705
commit
9fecaa7bca
8 changed files with 256 additions and 6 deletions
|
|
@ -189,3 +189,9 @@ export async function getAccessToken(): Promise<string | null> {
|
|||
export function getUsername(): string | undefined {
|
||||
return username ?? undefined;
|
||||
}
|
||||
|
||||
/** fid ของเอกสารปัจจุบัน (จาก ticket ที่ sign-in ผ่าน) — null = ยังไม่ได้ sign-in หรือโหมด demo
|
||||
* ใช้เป็น tracking id ตอน Start Versioning (versionFlow.resolveTrackingId, 2026-11-05) */
|
||||
export function getCurrentFid(): string | null {
|
||||
return currentFid;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
* (known bug บน Mac บาง build — เขียนไม่ได้ก็ปล่อยผ่าน)
|
||||
*/
|
||||
|
||||
import { saveDocumentVerified } from "./documentService";
|
||||
|
||||
/** GUID คงที่ของ add-in — ต้องตรงเป๊ะกับ `AddInMetadata.AddInSignatureGuid` ฝั่ง backend (spec ส่วน 8) */
|
||||
export const ADDIN_SIGNATURE_GUID = "9B7A3C51-4E6F-4D2A-8B5C-1E0F2A3B4C5D";
|
||||
|
||||
|
|
@ -128,6 +130,60 @@ export async function stampTrackedProperties(input: StampPropertiesInput): Promi
|
|||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Auto-open taskpane (Office.AutoShowTaskpaneWithDocument) — 2026-11-05
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** ชื่อ setting ที่ทำให้ taskpane เด้งอัตโนมัติเมื่อเปิดเอกสารนี้ครั้งถัดไป — ต้องตรงกับ
|
||||
* `<TaskpaneId>` ใน manifest.template.xml และ `<we:property>` ที่ lib/lib-tmpl.ts stamp
|
||||
* ลง word/webextensions/ ฝั่ง server (learn.microsoft.com/.../automatically-open-a-task-pane-with-a-document) */
|
||||
export const AUTO_OPEN_SETTING = "Office.AutoShowTaskpaneWithDocument";
|
||||
|
||||
/** memo กัน double-call — detect() ถูกเรียกซ้ำ (ปุ่ม "ลองใหม่") ก็แชร์ promise เดิม;
|
||||
* ล้มแล้วล้างทิ้งให้ครั้งถัดไปได้ลองใหม่ */
|
||||
let autoOpenPromise: Promise<boolean> | null = null;
|
||||
|
||||
/**
|
||||
* tag เอกสารปัจจุบันให้ taskpane เด้งอัตโนมัติครั้งถัดไปที่เปิดไฟล์นี้ — **best-effort
|
||||
* ห้าม throw ทุกกรณี** (auto-open เป็น UX enhancement: ล้ม = ผู้ใช้กดปุ่มเองตามเดิม)
|
||||
* คืน true = setting มีอยู่แล้ว หรือเขียนสำเร็จ · false = ทำไม่สำเร็จ (เงียบ ไม่ใช่ error ของ UI)
|
||||
*/
|
||||
export function enableAutoOpenTaskpane(): Promise<boolean> {
|
||||
if (autoOpenPromise === null) {
|
||||
autoOpenPromise = (async () => {
|
||||
try {
|
||||
const settings = Office.context.document.settings;
|
||||
if (settings.get(AUTO_OPEN_SETTING) === true) return true; // idempotent — ไม่เขียนซ้ำ
|
||||
settings.set(AUTO_OPEN_SETTING, true);
|
||||
const saved = await new Promise<boolean>((resolve) => {
|
||||
settings.saveAsync((result) => resolve(result.status === Office.AsyncResultStatus.Succeeded));
|
||||
});
|
||||
if (!saved) {
|
||||
console.warn("auto-open: settings.saveAsync ไม่สำเร็จ — taskpane จะไม่เด้งครั้งถัดไป");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
// saveAsync เขียนเฉพาะ document ในหน่วยความจำ — persist ลงดิสก์จริงต้องตามด้วย native
|
||||
// save (ไฟล์ read-only/Protected View = ยอมแพ้เงียบ ๆ: auto-save 30 วิเป็น net รอง)
|
||||
await saveDocumentVerified();
|
||||
} catch (error) {
|
||||
console.warn("auto-open: บันทึกไฟล์ไม่สำเร็จ (read-only?) — setting อาจยังไม่ถึงดิสก์", error);
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
// นอก host Word (browser/SSR) หรือ host ไม่รองรับ Settings API — เงียบไป
|
||||
console.warn("auto-open: เขียน setting ไม่สำเร็จ (นอก Word หรือ host ไม่รองรับ)", error);
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
// ล้ม = ล้าง memo ให้ detect ครั้งถัดได้ลองใหม่ (ผ่านปุ่ม "ลองใหม่")
|
||||
void autoOpenPromise.then((ok) => {
|
||||
if (!ok) autoOpenPromise = null;
|
||||
});
|
||||
}
|
||||
return autoOpenPromise;
|
||||
}
|
||||
|
||||
/** GUID ใหม่สำหรับ tracking id / clientVersionId */
|
||||
export function generateGuid(): string {
|
||||
if (typeof crypto.randomUUID === "function") {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,17 @@ import {
|
|||
} from "./metadataService";
|
||||
import { uploadVersion } from "./versioningService";
|
||||
|
||||
/** 2026-11-05 — tracking id ใช้ **fid จาก ticket** (ตรงกับ docId ของทุก operation ฝั่ง server —
|
||||
* uploadVersion real mode ไม่ส่ง documentId ไป server อยู่แล้ว identity มาจาก Bearer) แทน GUID
|
||||
* สุ่มใหม่ · **ย้อนกลับเป็นแบบเดิม**: เปลี่ยนค่าเป็น "guid" (generateGuid ยังอยู่ — ไฟล์เก่า
|
||||
* ที่ stamp GUID ไว้แล้วยัง track ได้เพราะ detect อ่านค่าตาม property ไม่สนรูปแบบค่า) */
|
||||
const TRACKING_ID_SOURCE: "fid" | "guid" = "fid";
|
||||
|
||||
/** เลือกค่า tracking id ตามโหมด — pure แยกไว้ให้ unit test ได้ (ไม่มี fid = fallback GUID เดิม) */
|
||||
export function resolveTrackingId(currentFid: string | null): string {
|
||||
return TRACKING_ID_SOURCE === "fid" && currentFid !== null ? currentFid : generateGuid();
|
||||
}
|
||||
|
||||
export interface StartVersioningResult {
|
||||
hasPdf: boolean;
|
||||
documentId: string;
|
||||
|
|
@ -121,7 +132,7 @@ export async function startVersioning(
|
|||
// FR-17 — หยุด auto-save ตลอด flow กัน save แทรกกลาง stamp/capture แล้วค่อนคืนตอนจบ
|
||||
await autoSaveService.suspend();
|
||||
try {
|
||||
const documentId = generateGuid();
|
||||
const documentId = resolveTrackingId(authService.getCurrentFid());
|
||||
const clientVersionId = generateGuid();
|
||||
const timestamp = new Date().toISOString();
|
||||
|
||||
|
|
@ -147,6 +158,13 @@ export async function startVersioning(
|
|||
// 4) upload version แรก (แนบ templateId เมื่อเป็นไฟล์ร่าง — FR-16)
|
||||
onProgress?.("กำลังอัปโหลด version 1…");
|
||||
const result = await uploadCurrentFile(documentId, clientVersionId, message, onProgress, templateId);
|
||||
|
||||
// 5) final save (2026-11-05 bug fix) — การันตีว่าทุกอย่างที่ stamp ใน flow นี้ persist
|
||||
// ถึงดิสก์จริงก่อนจบ (เดิม save ครั้งเดียวช่วงขั้น 3 ก่อน upload — เคยเจอไฟล์ถูก stamp
|
||||
// แล้วได้ version แต่ดิสก์ไม่ได้รับค่า ปิดไฟล์กลับมาเปิดใหม่กลายเป็น untracked)
|
||||
onProgress?.("กำลังบันทึกไฟล์…");
|
||||
await saveDocumentVerified();
|
||||
|
||||
// FR-3 — สร้าง version สำเร็จ → เคลียร์ flag "มีแก้ไขที่ยังไม่เป็น version"
|
||||
autoSaveService.clearUncommittedSinceVersion();
|
||||
return { documentId, ...result };
|
||||
|
|
@ -206,11 +224,19 @@ export async function saveAndVersion(
|
|||
}
|
||||
}
|
||||
|
||||
// 6) final save (2026-11-05 bug fix) — ขั้น 5 เขียน XML part หลัง upload โดยไม่มี save ตาม
|
||||
// = เอกสาร dirty ตอน flow จบ ผู้ใช้ปิด Word โดยไม่ save เพิ่ม = ค่าที่ stamp ทั้ง flow
|
||||
// (รวม tracking properties ของขั้น 1) ไม่ถึงดิสก์ → เปิดใหม่กลายเป็น untracked —
|
||||
// ต้อง save ปิดท้ายหลังทุก mutation ก่อนคืนผล (และก่อน close เพราะหลัง close เรียก
|
||||
// Word.run ต่อไม่ได้)
|
||||
onProgress?.("กำลังบันทึกไฟล์…");
|
||||
await saveDocumentVerified();
|
||||
|
||||
// FR-3 — สร้าง version สำเร็จ → เคลียร์ flag "มีแก้ไขที่ยังไม่เป็น version"
|
||||
autoSaveService.clearUncommittedSinceVersion();
|
||||
|
||||
// 6) FR-18 — ปิดเอกสารเฉพาะเมื่อ upload สำเร็จแล้วเท่านั้น (ต้องอยู่หลังขั้น 5 เพราะหลัง close
|
||||
// เอกสารหมดสิทธิ์เรียก Word.run ต่อ)
|
||||
// 7) FR-18 — ปิดเอกสารเฉพาะเมื่อ upload สำเร็จแล้วเท่านั้น (ต้องอยู่หลังขั้น final save
|
||||
// เพราะหลัง close เอกสารหมดสิทธิ์เรียก Word.run ต่อ)
|
||||
if (closeAfter) {
|
||||
try {
|
||||
await closeDocument(DocumentCloseMode.Save);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue