v2.2.16: Astra Office UI Overhaul & Operations Floor
This commit is contained in:
@@ -49,28 +49,99 @@ export interface CalendarConfig {
|
||||
connectedAt?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings + globalState 두 곳에서 읽어 merge.
|
||||
*
|
||||
* • VS Code Settings (g1nation.google.*) — 사용자가 직접 편집 가능한 필드:
|
||||
* clientId, clientSecret, calendarId, defaultDurationMinutes, icalUrl, daysAhead
|
||||
* 이 값이 채워져 있으면 globalState 의 같은 필드보다 *우선*.
|
||||
*
|
||||
* • globalState (CAL_CONFIG_KEY) — 마법사가 자동 관리하는 secret / runtime 필드:
|
||||
* refreshToken, accessToken, accessTokenExpiresAt, connectedAs, connectedAt, lastFetchAt
|
||||
* 사용자는 settings 에서 안 보임. 마법사가 OAuth 완료 후 자동 기록.
|
||||
*
|
||||
* • 옛 사용자 호환: globalState 에 clientId 같은 게 남아있어도 settings 가 비면
|
||||
* globalState 값으로 fallback. 명시적으로 settings 에 비워두면 globalState 도 무시.
|
||||
*/
|
||||
export function readCalendarConfig(context: vscode.ExtensionContext): CalendarConfig {
|
||||
const raw = context.globalState.get(CAL_CONFIG_KEY) as Partial<CalendarConfig> | undefined;
|
||||
const raw = (context.globalState.get(CAL_CONFIG_KEY) as Partial<CalendarConfig> | undefined) ?? {};
|
||||
const s = vscode.workspace.getConfiguration('g1nation.google');
|
||||
const fromSettings = <T>(key: string): T | undefined => {
|
||||
const v = s.get<T>(key);
|
||||
// 빈 문자열은 "미설정" 으로 취급 — 사용자가 지운 케이스.
|
||||
if (typeof v === 'string' && v.trim() === '') return undefined;
|
||||
return v;
|
||||
};
|
||||
const clientId = fromSettings<string>('clientId') ?? (typeof raw.clientId === 'string' ? raw.clientId : undefined);
|
||||
const clientSecret = fromSettings<string>('clientSecret') ?? (typeof raw.clientSecret === 'string' ? raw.clientSecret : undefined);
|
||||
const calendarId = fromSettings<string>('calendarId') ?? (typeof raw.calendarId === 'string' ? raw.calendarId : undefined);
|
||||
const defaultDurationMinutes = fromSettings<number>('defaultEventDurationMinutes')
|
||||
?? (typeof raw.defaultDurationMinutes === 'number' ? raw.defaultDurationMinutes : undefined);
|
||||
const icalUrl = fromSettings<string>('icalUrl') ?? (typeof raw.icalUrl === 'string' ? raw.icalUrl : '');
|
||||
const daysAhead = fromSettings<number>('icalDaysAhead') ?? (typeof raw.daysAhead === 'number' && raw.daysAhead > 0 ? raw.daysAhead : 14);
|
||||
return {
|
||||
icalUrl: typeof raw?.icalUrl === 'string' ? raw.icalUrl : '',
|
||||
daysAhead: typeof raw?.daysAhead === 'number' && raw.daysAhead > 0 ? raw.daysAhead : 14,
|
||||
lastFetchAt: typeof raw?.lastFetchAt === 'string' ? raw.lastFetchAt : undefined,
|
||||
clientId: typeof raw?.clientId === 'string' ? raw.clientId : undefined,
|
||||
clientSecret: typeof raw?.clientSecret === 'string' ? raw.clientSecret : undefined,
|
||||
refreshToken: typeof raw?.refreshToken === 'string' ? raw.refreshToken : undefined,
|
||||
calendarId: typeof raw?.calendarId === 'string' ? raw.calendarId : undefined,
|
||||
defaultDurationMinutes: typeof raw?.defaultDurationMinutes === 'number' ? raw.defaultDurationMinutes : undefined,
|
||||
accessToken: typeof raw?.accessToken === 'string' ? raw.accessToken : undefined,
|
||||
accessTokenExpiresAt: typeof raw?.accessTokenExpiresAt === 'number' ? raw.accessTokenExpiresAt : undefined,
|
||||
connectedAs: typeof raw?.connectedAs === 'string' ? raw.connectedAs : undefined,
|
||||
connectedAt: typeof raw?.connectedAt === 'string' ? raw.connectedAt : undefined,
|
||||
icalUrl: icalUrl ?? '',
|
||||
daysAhead: daysAhead ?? 14,
|
||||
lastFetchAt: typeof raw.lastFetchAt === 'string' ? raw.lastFetchAt : undefined,
|
||||
clientId,
|
||||
clientSecret,
|
||||
refreshToken: typeof raw.refreshToken === 'string' ? raw.refreshToken : undefined,
|
||||
calendarId,
|
||||
defaultDurationMinutes,
|
||||
accessToken: typeof raw.accessToken === 'string' ? raw.accessToken : undefined,
|
||||
accessTokenExpiresAt: typeof raw.accessTokenExpiresAt === 'number' ? raw.accessTokenExpiresAt : undefined,
|
||||
connectedAs: typeof raw.connectedAs === 'string' ? raw.connectedAs : undefined,
|
||||
connectedAt: typeof raw.connectedAt === 'string' ? raw.connectedAt : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch 의 필드를 settings 또는 globalState 의 적절한 곳에 분기 저장.
|
||||
* • settings 로 가는 것 (사용자 편집 가능): clientId, clientSecret, calendarId,
|
||||
* defaultDurationMinutes, icalUrl, daysAhead
|
||||
* • globalState 로 가는 것 (secret / runtime): refreshToken, accessToken,
|
||||
* accessTokenExpiresAt, connectedAs, connectedAt, lastFetchAt
|
||||
*
|
||||
* 한 번에 양쪽을 patch 해도 OK — 분기 자동.
|
||||
*/
|
||||
export async function writeCalendarConfig(context: vscode.ExtensionContext, patch: Partial<CalendarConfig>): Promise<void> {
|
||||
const cur = readCalendarConfig(context);
|
||||
const next: CalendarConfig = { ...cur, ...patch };
|
||||
await context.globalState.update(CAL_CONFIG_KEY, next);
|
||||
// Settings (g1nation.google.*) 로 가는 필드들.
|
||||
const s = vscode.workspace.getConfiguration('g1nation.google');
|
||||
const settingsKeys: Array<[keyof CalendarConfig, string]> = [
|
||||
['clientId', 'clientId'],
|
||||
['clientSecret', 'clientSecret'],
|
||||
['calendarId', 'calendarId'],
|
||||
['defaultDurationMinutes', 'defaultEventDurationMinutes'],
|
||||
['icalUrl', 'icalUrl'],
|
||||
['daysAhead', 'icalDaysAhead'],
|
||||
];
|
||||
for (const [src, dst] of settingsKeys) {
|
||||
if (src in patch) {
|
||||
const v = (patch as any)[src];
|
||||
// undefined → settings 에서 제거 (default 로 복귀). 빈 문자열도 동일 취급.
|
||||
const toWrite = (v === undefined || v === '') ? undefined : v;
|
||||
try { await s.update(dst, toWrite, vscode.ConfigurationTarget.Global); }
|
||||
catch { /* settings 쓰기 실패 시 globalState 로 fallback (다음 read 가 globalState 봄). */
|
||||
const cur = (context.globalState.get(CAL_CONFIG_KEY) as Partial<CalendarConfig>) ?? {};
|
||||
await context.globalState.update(CAL_CONFIG_KEY, { ...cur, [src]: v });
|
||||
}
|
||||
}
|
||||
}
|
||||
// GlobalState 로 가는 secret / runtime 필드.
|
||||
const globalKeys: Array<keyof CalendarConfig> = [
|
||||
'refreshToken', 'accessToken', 'accessTokenExpiresAt',
|
||||
'connectedAs', 'connectedAt', 'lastFetchAt',
|
||||
];
|
||||
const cur = (context.globalState.get(CAL_CONFIG_KEY) as Partial<CalendarConfig>) ?? {};
|
||||
const next: Partial<CalendarConfig> = { ...cur };
|
||||
let dirty = false;
|
||||
for (const k of globalKeys) {
|
||||
if (k in patch) {
|
||||
(next as any)[k] = (patch as any)[k];
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
if (dirty) await context.globalState.update(CAL_CONFIG_KEY, next);
|
||||
}
|
||||
|
||||
/** 회사 디렉토리 내부 캐시 파일 경로. workspace 없으면 globalStorage 로 fallback. */
|
||||
|
||||
Reference in New Issue
Block a user