179 lines
5.4 KiB
JavaScript
179 lines
5.4 KiB
JavaScript
// uiComponents.js
|
||
// UI du mini gestionnaire de mots de passe.
|
||
|
||
import St from 'gi://St';
|
||
import Gio from 'gi://Gio';
|
||
import GLib from 'gi://GLib';
|
||
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
||
import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
|
||
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
|
||
import * as ExtensionUtils from 'resource:///org/gnome/shell/misc/extensionUtils.js';
|
||
|
||
const Clutter = imports.gi.Clutter;
|
||
const Clipboard = St.Clipboard.get_default();
|
||
const ClipboardType = St.ClipboardType.CLIPBOARD;
|
||
|
||
function copyToClipboard(text) {
|
||
if (text)
|
||
Clipboard.set_text(ClipboardType, text);
|
||
}
|
||
|
||
export class PasswordEntryRow extends PopupMenu.PopupBaseMenuItem {
|
||
constructor(entry) {
|
||
super({ reactive: true });
|
||
|
||
const box = new St.BoxLayout({ vertical: false, x_expand: true });
|
||
|
||
let labelWidget;
|
||
if (entry.url) {
|
||
labelWidget = new St.Label({
|
||
text: entry.label,
|
||
style_class: 'password-applet-label-link',
|
||
x_expand: true
|
||
});
|
||
labelWidget.clutter_text.underline = true;
|
||
labelWidget.connect('button-press-event', () => {
|
||
Gio.AppInfo.launch_default_for_uri(entry.url, null);
|
||
});
|
||
} else {
|
||
labelWidget = new St.Label({ text: entry.label, x_expand: true });
|
||
}
|
||
|
||
box.add_child(labelWidget);
|
||
|
||
box.add_child(new St.Label({ text: entry.username, x_expand: true }));
|
||
box.add_child(new St.Label({ text: '••••••••' }));
|
||
|
||
const uBtn = new St.Button({ label: 'U' });
|
||
uBtn.connect('clicked', () => copyToClipboard(entry.username));
|
||
box.add_child(uBtn);
|
||
|
||
const pBtn = new St.Button({ label: 'P' });
|
||
pBtn.connect('clicked', () => copyToClipboard(entry.password_enc));
|
||
box.add_child(pBtn);
|
||
|
||
this.actor.add_child(box);
|
||
}
|
||
}
|
||
|
||
export class PasswordListSection extends PopupMenu.PopupMenuSection {
|
||
setEntries(entries) {
|
||
this.removeAll();
|
||
|
||
if (!entries.length) {
|
||
this.addMenuItem(new PopupMenu.PopupMenuItem(
|
||
'Aucune entrée', { reactive: false }
|
||
));
|
||
return;
|
||
}
|
||
|
||
for (const e of entries)
|
||
this.addMenuItem(new PasswordEntryRow(e));
|
||
}
|
||
}
|
||
|
||
export class AddEntrySection extends PopupMenu.PopupMenuSection {
|
||
constructor(repo, refreshCb) {
|
||
super();
|
||
this._repo = repo;
|
||
this._refreshCb = refreshCb;
|
||
this._showAddButton();
|
||
}
|
||
|
||
_showAddButton() {
|
||
this.removeAll();
|
||
const item = new PopupMenu.PopupMenuItem('➕ Ajouter une entrée');
|
||
item.connect('activate', () => this._showForm());
|
||
this.addMenuItem(item);
|
||
}
|
||
|
||
_showForm() {
|
||
this.removeAll();
|
||
const wrapper = new PopupMenu.PopupBaseMenuItem({ reactive: false });
|
||
const box = new St.BoxLayout({ vertical: true });
|
||
|
||
const mkEntry = hint =>
|
||
new St.Entry({ hint_text: hint, style_class: 'password-applet-entry' });
|
||
|
||
const label = mkEntry("Label");
|
||
const user = mkEntry("User");
|
||
const pwd = mkEntry("Password");
|
||
const url = mkEntry("URL");
|
||
const notes = mkEntry("Notes");
|
||
|
||
const mkRow = (name, widget) => {
|
||
const row = new St.BoxLayout();
|
||
row.add_child(new St.Label({ text: name + " : " }));
|
||
row.add_child(widget);
|
||
return row;
|
||
};
|
||
|
||
box.add_child(mkRow("Label", label));
|
||
box.add_child(mkRow("User", user));
|
||
box.add_child(mkRow("Password", pwd));
|
||
box.add_child(mkRow("URL", url));
|
||
box.add_child(mkRow("Notes", notes));
|
||
|
||
const btnRow = new St.BoxLayout();
|
||
|
||
const save = new St.Button({ label: "Enregistrer" });
|
||
save.connect('clicked', () => {
|
||
const entry = {
|
||
label: label.get_text().trim(),
|
||
username: user.get_text().trim(),
|
||
password: pwd.get_text().trim(),
|
||
url: url.get_text().trim(),
|
||
notes: notes.get_text().trim()
|
||
};
|
||
|
||
if (!entry.label || !entry.username || !entry.password) {
|
||
Main.notify("Password Applet", "Champs obligatoires manquants");
|
||
return;
|
||
}
|
||
|
||
this._repo.addEntry(entry);
|
||
this._refreshCb();
|
||
this._showAddButton();
|
||
});
|
||
|
||
const cancel = new St.Button({ label: "Annuler" });
|
||
cancel.connect('clicked', () => this._showAddButton());
|
||
|
||
btnRow.add_child(save);
|
||
btnRow.add_child(cancel);
|
||
|
||
box.add_child(btnRow);
|
||
wrapper.actor.add_child(box);
|
||
|
||
this.addMenuItem(wrapper);
|
||
}
|
||
}
|
||
|
||
export class PasswordApplet extends PanelMenu.Button {
|
||
constructor(repo) {
|
||
super(0.0, 'PasswordApplet', false);
|
||
|
||
this._repo = repo;
|
||
|
||
this.add_child(new St.Icon({
|
||
icon_name: 'dialog-password-symbolic',
|
||
style_class: 'system-status-icon'
|
||
}));
|
||
|
||
this._list = new PasswordListSection();
|
||
this.menu.addMenuItem(this._list);
|
||
|
||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||
|
||
this._add = new AddEntrySection(repo, () => this._reload());
|
||
this.menu.addMenuItem(this._add);
|
||
|
||
this._reload();
|
||
}
|
||
|
||
_reload() {
|
||
const entries = this._repo.listEntries();
|
||
this._list.setEntries(entries);
|
||
}
|
||
}
|