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:
Suphonchai Phoonsawat 2026-11-05 12:42:02 +07:00
parent 02894f2705
commit 9fecaa7bca
8 changed files with 256 additions and 6 deletions

View file

@ -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") {