Added the ability to delete history entries and fixed a small react updating bug
parent
2b54266ff7
commit
bd3810fb49
|
@ -313,6 +313,41 @@ export class ConnectionHistory {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteConnectionAttempts(target: string, targetType: "address" | "server-unique-id") {
|
||||||
|
if(!this.database) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const transaction = this.database.transaction(["attempt-history"], "readwrite");
|
||||||
|
const store = transaction.objectStore("attempt-history");
|
||||||
|
|
||||||
|
const cursor = store.index(targetType === "server-unique-id" ? "serverUniqueId" : "targetAddress").openCursor(target);
|
||||||
|
const promises = [];
|
||||||
|
while(true) {
|
||||||
|
const entry = await new Promise<IDBCursorWithValue | null>((resolve, reject) => {
|
||||||
|
cursor.onsuccess = () => resolve(cursor.result);
|
||||||
|
cursor.onerror = () => reject(cursor.error);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!entry) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
promises.push(new Promise(resolve => {
|
||||||
|
const deleteRequest = entry.delete();
|
||||||
|
deleteRequest.onsuccess = resolve;
|
||||||
|
deleteRequest.onerror = () => {
|
||||||
|
logWarn(LogCategory.GENERAL, tr("Failed to delete a connection attempt: %o"), deleteRequest.error);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
entry.continue();
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query the server info of a given server unique id
|
* Query the server info of a given server unique id
|
||||||
* @param serverUniqueId
|
* @param serverUniqueId
|
||||||
|
|
|
@ -181,6 +181,16 @@ class ConnectController {
|
||||||
settings.changeGlobal(Settings.KEY_CONNECT_SHOW_HISTORY, event.enabled);
|
settings.changeGlobal(Settings.KEY_CONNECT_SHOW_HISTORY, event.enabled);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.uiEvents.on("action_delete_history", event => {
|
||||||
|
connectionHistory.deleteConnectionAttempts(event.target, event.targetType).then(() => {
|
||||||
|
this.history = undefined;
|
||||||
|
this.sendProperty("history").then(undefined);
|
||||||
|
}).catch(error => {
|
||||||
|
logWarn(LogCategory.GENERAL, tr("Failed to delete connection attempts: %o"), error);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
this.uiEvents.on("action_manage_profiles", () => {
|
this.uiEvents.on("action_manage_profiles", () => {
|
||||||
/* TODO: This is more a hack. Proper solution is that the connection profiles fire events if they've been changed... */
|
/* TODO: This is more a hack. Proper solution is that the connection profiles fire events if they've been changed... */
|
||||||
const modal = spawnSettingsModal("identity-profiles");
|
const modal = spawnSettingsModal("identity-profiles");
|
||||||
|
@ -199,13 +209,16 @@ class ConnectController {
|
||||||
this.setSelectedProfile(profile);
|
this.setSelectedProfile(profile);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.uiEvents.on("action_set_address", event => this.setSelectedAddress(event.address, event.validate));
|
this.uiEvents.on("action_set_address", event => this.setSelectedAddress(event.address, event.validate, event.updateUi));
|
||||||
|
|
||||||
this.uiEvents.on("action_set_nickname", event => {
|
this.uiEvents.on("action_set_nickname", event => {
|
||||||
if(this.currentNickname !== event.nickname) {
|
if(this.currentNickname !== event.nickname) {
|
||||||
this.currentNickname = event.nickname;
|
this.currentNickname = event.nickname;
|
||||||
this.sendProperty("nickname").then(undefined);
|
|
||||||
settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, event.nickname);
|
settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, event.nickname);
|
||||||
|
|
||||||
|
if(event.updateUi) {
|
||||||
|
this.sendProperty("nickname").then(undefined);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.validateStates["nickname"] = event.validate;
|
this.validateStates["nickname"] = event.validate;
|
||||||
|
@ -219,7 +232,9 @@ class ConnectController {
|
||||||
|
|
||||||
this.currentPassword = event.password;
|
this.currentPassword = event.password;
|
||||||
this.currentPasswordHashed = event.hashed;
|
this.currentPasswordHashed = event.hashed;
|
||||||
this.sendProperty("password").then(undefined);
|
if(event.updateUi) {
|
||||||
|
this.sendProperty("password").then(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
this.validateStates["password"] = true;
|
this.validateStates["password"] = true;
|
||||||
this.updateValidityStates();
|
this.updateValidityStates();
|
||||||
|
@ -279,12 +294,15 @@ class ConnectController {
|
||||||
this.sendProperty("nickname").then(undefined);
|
this.sendProperty("nickname").then(undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelectedAddress(address: string | undefined, validate: boolean) {
|
setSelectedAddress(address: string | undefined, validate: boolean, updateUi: boolean) {
|
||||||
if(this.currentAddress !== address) {
|
if(this.currentAddress !== address) {
|
||||||
this.currentAddress = address;
|
this.currentAddress = address;
|
||||||
this.sendProperty("address").then(undefined);
|
|
||||||
settings.changeGlobal(Settings.KEY_CONNECT_ADDRESS, address);
|
settings.changeGlobal(Settings.KEY_CONNECT_ADDRESS, address);
|
||||||
this.setSelectedHistoryId(-1);
|
this.setSelectedHistoryId(-1);
|
||||||
|
|
||||||
|
if(updateUi) {
|
||||||
|
this.sendProperty("address").then(undefined);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.validateStates["address"] = validate;
|
this.validateStates["address"] = validate;
|
||||||
|
@ -301,7 +319,7 @@ class ConnectController {
|
||||||
settings.changeGlobal(Settings.KEY_CONNECT_PROFILE, profile.id);
|
settings.changeGlobal(Settings.KEY_CONNECT_PROFILE, profile.id);
|
||||||
|
|
||||||
/* Clear out the nickname on profile switch and use the default nickname */
|
/* Clear out the nickname on profile switch and use the default nickname */
|
||||||
this.uiEvents.fire("action_set_nickname", { nickname: undefined, validate: true });
|
this.uiEvents.fire("action_set_nickname", { nickname: undefined, validate: true, updateUi: true });
|
||||||
|
|
||||||
this.validateStates["profile"] = true;
|
this.validateStates["profile"] = true;
|
||||||
this.updateValidityStates();
|
this.updateValidityStates();
|
||||||
|
@ -367,7 +385,7 @@ export function spawnConnectModalNew(options: ConnectModalOptions) {
|
||||||
const controller = new ConnectController();
|
const controller = new ConnectController();
|
||||||
|
|
||||||
if(typeof options.selectedAddress === "string") {
|
if(typeof options.selectedAddress === "string") {
|
||||||
controller.setSelectedAddress(options.selectedAddress, false);
|
controller.setSelectedAddress(options.selectedAddress, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(typeof options.selectedProfile === "object") {
|
if(typeof options.selectedProfile === "object") {
|
||||||
|
|
|
@ -69,9 +69,9 @@ export interface ConnectUiEvents {
|
||||||
target: string,
|
target: string,
|
||||||
targetType: "address" | "server-unique-id"
|
targetType: "address" | "server-unique-id"
|
||||||
},
|
},
|
||||||
action_set_nickname: { nickname: string, validate: boolean },
|
action_set_nickname: { nickname: string, validate: boolean, updateUi: boolean },
|
||||||
action_set_address: { address: string, validate: boolean },
|
action_set_address: { address: string, validate: boolean, updateUi: boolean },
|
||||||
action_set_password: { password: string, hashed: boolean },
|
action_set_password: { password: string, hashed: boolean, updateUi: boolean },
|
||||||
|
|
||||||
query_property: {
|
query_property: {
|
||||||
property: keyof ConnectProperties
|
property: keyof ConnectProperties
|
||||||
|
|
|
@ -24,7 +24,7 @@ const ConnectDefaultNewTabContext = React.createContext<boolean>(false);
|
||||||
|
|
||||||
const cssStyle = require("./Renderer.scss");
|
const cssStyle = require("./Renderer.scss");
|
||||||
|
|
||||||
function useProperty<T extends keyof ConnectProperties, V>(key: T, defaultValue: V) : ConnectProperties[T] | V {
|
function useProperty<T extends keyof ConnectProperties, V>(key: T, defaultValue: V) : [ConnectProperties[T] | V, (value: ConnectProperties[T]) => void] {
|
||||||
const events = useContext(EventContext);
|
const events = useContext(EventContext);
|
||||||
const [ value, setValue ] = useState<ConnectProperties[T] | V>(() => {
|
const [ value, setValue ] = useState<ConnectProperties[T] | V>(() => {
|
||||||
events.fire("query_property", { property: key });
|
events.fire("query_property", { property: key });
|
||||||
|
@ -32,7 +32,7 @@ function useProperty<T extends keyof ConnectProperties, V>(key: T, defaultValue:
|
||||||
});
|
});
|
||||||
events.reactUse("notify_property", event => event.property === key && setValue(event.value as any));
|
events.reactUse("notify_property", event => event.property === key && setValue(event.value as any));
|
||||||
|
|
||||||
return value;
|
return [value, setValue];
|
||||||
}
|
}
|
||||||
|
|
||||||
function usePropertyValid<T extends keyof PropertyValidState>(key: T, defaultValue: PropertyValidState[T]) : PropertyValidState[T] {
|
function usePropertyValid<T extends keyof PropertyValidState>(key: T, defaultValue: PropertyValidState[T]) : PropertyValidState[T] {
|
||||||
|
@ -48,7 +48,7 @@ function usePropertyValid<T extends keyof PropertyValidState>(key: T, defaultVal
|
||||||
|
|
||||||
const InputServerAddress = () => {
|
const InputServerAddress = () => {
|
||||||
const events = useContext(EventContext);
|
const events = useContext(EventContext);
|
||||||
const address = useProperty("address", undefined);
|
const [address, setAddress] = useProperty("address", undefined);
|
||||||
const valid = usePropertyValid("address", true);
|
const valid = usePropertyValid("address", true);
|
||||||
const newTab = useContext(ConnectDefaultNewTabContext);
|
const newTab = useContext(ConnectDefaultNewTabContext);
|
||||||
|
|
||||||
|
@ -64,16 +64,25 @@ const InputServerAddress = () => {
|
||||||
|
|
||||||
invalid={valid ? undefined : <Translatable>Please enter a valid server address</Translatable>}
|
invalid={valid ? undefined : <Translatable>Please enter a valid server address</Translatable>}
|
||||||
|
|
||||||
onInput={value => events.fire("action_set_address", { address: value, validate: false })}
|
onInput={value => {
|
||||||
onBlur={() => events.fire("action_set_address", { address: address?.currentAddress, validate: true })}
|
setAddress({ currentAddress: value, defaultAddress: address.defaultAddress });
|
||||||
onEnter={() => events.fire("action_connect", { newTab })}
|
events.fire("action_set_address", { address: value, validate: true, updateUi: false });
|
||||||
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
events.fire("action_set_address", { address: address?.currentAddress, validate: true, updateUi: true });
|
||||||
|
}}
|
||||||
|
onEnter={() => {
|
||||||
|
/* Setting the address just to ensure */
|
||||||
|
events.fire("action_set_address", { address: address?.currentAddress, validate: true, updateUi: true });
|
||||||
|
events.fire("action_connect", { newTab });
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const InputServerPassword = () => {
|
const InputServerPassword = () => {
|
||||||
const events = useContext(EventContext);
|
const events = useContext(EventContext);
|
||||||
const password = useProperty("password", undefined);
|
const [password, setPassword] = useProperty("password", undefined);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FlatInputField
|
<FlatInputField
|
||||||
|
@ -83,14 +92,22 @@ const InputServerPassword = () => {
|
||||||
type={"password"}
|
type={"password"}
|
||||||
label={<Translatable>Server password</Translatable>}
|
label={<Translatable>Server password</Translatable>}
|
||||||
labelType={password?.hashed ? "static" : "floating"}
|
labelType={password?.hashed ? "static" : "floating"}
|
||||||
onInput={value => events.fire("action_set_password", { password: value, hashed: false })}
|
onInput={value => {
|
||||||
|
setPassword({ password: value, hashed: false });
|
||||||
|
events.fire("action_set_password", { password: value, hashed: false, updateUi: false });
|
||||||
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
if(password) {
|
||||||
|
events.fire("action_set_password", { password: password.password, hashed: password.hashed, updateUi: true });
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const InputNickname = () => {
|
const InputNickname = () => {
|
||||||
const events = useContext(EventContext);
|
const events = useContext(EventContext);
|
||||||
const nickname = useProperty("nickname", undefined);
|
const [nickname, setNickname] = useProperty("nickname", undefined);
|
||||||
const valid = usePropertyValid("nickname", true);
|
const valid = usePropertyValid("nickname", true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -101,15 +118,18 @@ const InputNickname = () => {
|
||||||
label={<Translatable>Nickname</Translatable>}
|
label={<Translatable>Nickname</Translatable>}
|
||||||
labelType={"static"}
|
labelType={"static"}
|
||||||
invalid={valid ? undefined : <Translatable>Nickname too short or too long</Translatable>}
|
invalid={valid ? undefined : <Translatable>Nickname too short or too long</Translatable>}
|
||||||
onInput={value => events.fire("action_set_nickname", { nickname: value, validate: false })}
|
onInput={value => {
|
||||||
onBlur={() => events.fire("action_set_nickname", { nickname: nickname?.currentNickname, validate: true })}
|
setNickname({ currentNickname: value, defaultNickname: nickname.defaultNickname });
|
||||||
|
events.fire("action_set_nickname", { nickname: value, validate: true, updateUi: false });
|
||||||
|
}}
|
||||||
|
onBlur={() => events.fire("action_set_nickname", { nickname: nickname?.currentNickname, validate: true, updateUi: true })}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const InputProfile = () => {
|
const InputProfile = () => {
|
||||||
const events = useContext(EventContext);
|
const events = useContext(EventContext);
|
||||||
const profiles = useProperty("profiles", undefined);
|
const [profiles] = useProperty("profiles", undefined);
|
||||||
const selectedProfile = profiles?.profiles.find(profile => profile.id === profiles?.selected);
|
const selectedProfile = profiles?.profiles.find(profile => profile.id === profiles?.selected);
|
||||||
|
|
||||||
let invalidMarker;
|
let invalidMarker;
|
||||||
|
@ -343,7 +363,7 @@ const HistoryTableEntry = React.memo((props: { entry: ConnectHistoryEntry, selec
|
||||||
});
|
});
|
||||||
|
|
||||||
const HistoryTable = () => {
|
const HistoryTable = () => {
|
||||||
const history = useProperty("history", undefined);
|
const [history] = useProperty("history", undefined);
|
||||||
let body;
|
let body;
|
||||||
|
|
||||||
if(history) {
|
if(history) {
|
||||||
|
|
Loading…
Reference in New Issue