UNPKG

5.08 kBPlain TextView Raw
1// *****************************************************************************
2// Copyright (C) 2017 TypeFox and others.
3//
4// This program and the accompanying materials are made available under the
5// terms of the Eclipse Public License v. 2.0 which is available at
6// http://www.eclipse.org/legal/epl-2.0.
7//
8// This Source Code may also be made available under the following Secondary
9// Licenses when the conditions for such availability set forth in the Eclipse
10// Public License v. 2.0 are satisfied: GNU General Public License, version 2
11// with the GNU Classpath Exception which is available at
12// https://www.gnu.org/software/classpath/license.html.
13//
14// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15// *****************************************************************************
16
17import { inject, injectable, postConstruct } from 'inversify';
18import { ILogger } from '../common/logger';
19import { MessageService } from '../common/message-service';
20import { WindowService } from './window/window-service';
21import { environment } from '@theia/application-package/lib/environment';
22
23export const StorageService = Symbol('IStorageService');
24/**
25 * The storage service provides an interface to some data storage that allows extensions to keep state among sessions.
26 */
27export interface StorageService {
28
29 /**
30 * Stores the given data under the given key.
31 */
32 setData<T>(key: string, data: T): Promise<void>;
33
34 /**
35 * Returns the data stored for the given key or the provided default value if nothing is stored for the given key.
36 */
37 getData<T>(key: string, defaultValue: T): Promise<T>;
38 getData<T>(key: string): Promise<T | undefined>;
39}
40
41interface LocalStorage {
42 // eslint-disable-next-line @typescript-eslint/no-explicit-any
43 [key: string]: any;
44}
45
46@injectable()
47export class LocalStorageService implements StorageService {
48 private storage: LocalStorage;
49
50 @inject(ILogger) protected logger: ILogger;
51 @inject(MessageService) protected readonly messageService: MessageService;
52 @inject(WindowService) protected readonly windowService: WindowService;
53
54 @postConstruct()
55 protected init(): void {
56 if (typeof window !== 'undefined' && window.localStorage) {
57 this.storage = window.localStorage;
58 this.testLocalStorage();
59 } else {
60 this.logger.warn(log => log("The browser doesn't support localStorage. state will not be persisted across sessions."));
61 this.storage = {};
62 }
63 }
64
65 setData<T>(key: string, data?: T): Promise<void> {
66 if (data !== undefined) {
67 try {
68 this.storage[this.prefix(key)] = JSON.stringify(data);
69 } catch (e) {
70 this.showDiskQuotaExceededMessage();
71 }
72 } else {
73 delete this.storage[this.prefix(key)];
74 }
75 return Promise.resolve();
76 }
77
78 getData<T>(key: string, defaultValue?: T): Promise<T | undefined> {
79 const result = this.storage[this.prefix(key)];
80 if (result === undefined) {
81 return Promise.resolve(defaultValue);
82 }
83 return Promise.resolve(JSON.parse(result));
84 }
85
86 protected prefix(key: string): string {
87 if (environment.electron.is()) {
88 return `theia:${key}`;
89 }
90 const pathname = typeof window === 'undefined' ? '' : window.location.pathname;
91 return `theia:${pathname}:${key}`;
92 }
93
94 private async showDiskQuotaExceededMessage(): Promise<void> {
95 const READ_INSTRUCTIONS_ACTION = 'Read Instructions';
96 const CLEAR_STORAGE_ACTION = 'Clear Local Storage';
97 const ERROR_MESSAGE = `Your preferred browser's local storage is almost full.
98 To be able to save your current workspace layout or data, you may need to free up some space.
99 You can refer to Theia's documentation page for instructions on how to manually clean
100 your browser's local storage or choose to clear all.`;
101 this.messageService.warn(ERROR_MESSAGE, READ_INSTRUCTIONS_ACTION, CLEAR_STORAGE_ACTION).then(async selected => {
102 if (READ_INSTRUCTIONS_ACTION === selected) {
103 this.windowService.openNewWindow('https://github.com/eclipse-theia/theia/wiki/Cleaning-Local-Storage', { external: true });
104 } else if (CLEAR_STORAGE_ACTION === selected) {
105 this.clearStorage();
106 }
107 });
108 }
109
110 /**
111 * Verify if there is still some spaces left to save another workspace configuration into the local storage of your browser.
112 * If we are close to the limit, use a dialog to notify the user.
113 */
114 private testLocalStorage(): void {
115 const keyTest = this.prefix('Test');
116 try {
117 this.storage[keyTest] = JSON.stringify(new Array(60000));
118 } catch (error) {
119 this.showDiskQuotaExceededMessage();
120 } finally {
121 this.storage.removeItem(keyTest);
122 }
123 }
124
125 private clearStorage(): void {
126 this.storage.clear();
127 }
128
129}