Added the ability to delete history entries and fixed a small react updating bug

master
WolverinDEV 2021-01-10 14:55:48 +01:00
parent 2b54266ff7
commit bd3810fb49
4 changed files with 96 additions and 23 deletions

View File

@ -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
* @param serverUniqueId

View File

@ -181,6 +181,16 @@ class ConnectController {
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", () => {
/* 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");
@ -199,13 +209,16 @@ class ConnectController {
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 => {
if(this.currentNickname !== event.nickname) {
this.currentNickname = event.nickname;
this.sendProperty("nickname").then(undefined);
settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, event.nickname);
if(event.updateUi) {
this.sendProperty("nickname").then(undefined);
}
}
this.validateStates["nickname"] = event.validate;
@ -219,7 +232,9 @@ class ConnectController {
this.currentPassword = event.password;
this.currentPasswordHashed = event.hashed;
this.sendProperty("password").then(undefined);
if(event.updateUi) {
this.sendProperty("password").then(undefined);
}
this.validateStates["password"] = true;
this.updateValidityStates();
@ -279,12 +294,15 @@ class ConnectController {
this.sendProperty("nickname").then(undefined);
}
setSelectedAddress(address: string | undefined, validate: boolean) {
setSelectedAddress(address: string | undefined, validate: boolean, updateUi: boolean) {
if(this.currentAddress !== address) {
this.currentAddress = address;
this.sendProperty("address").then(undefined);
settings.changeGlobal(Settings.KEY_CONNECT_ADDRESS, address);
this.setSelectedHistoryId(-1);
if(updateUi) {
this.sendProperty("address").then(undefined);
}
}
this.validateStates["address"] = validate;
@ -301,7 +319,7 @@ class ConnectController {
settings.changeGlobal(Settings.KEY_CONNECT_PROFILE, profile.id);
/* 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.updateValidityStates();
@ -367,7 +385,7 @@ export function spawnConnectModalNew(options: ConnectModalOptions) {
const controller = new ConnectController();
if(typeof options.selectedAddress === "string") {
controller.setSelectedAddress(options.selectedAddress, false);
controller.setSelectedAddress(options.selectedAddress, false, true);
}
if(typeof options.selectedProfile === "object") {

View File

@ -69,9 +69,9 @@ export interface ConnectUiEvents {
target: string,
targetType: "address" | "server-unique-id"
},
action_set_nickname: { nickname: string, validate: boolean },
action_set_address: { address: string, validate: boolean },
action_set_password: { password: string, hashed: boolean },
action_set_nickname: { nickname: string, validate: boolean, updateUi: boolean },
action_set_address: { address: string, validate: boolean, updateUi: boolean },
action_set_password: { password: string, hashed: boolean, updateUi: boolean },
query_property: {
property: keyof ConnectProperties

View File

@ -24,7 +24,7 @@ const ConnectDefaultNewTabContext = React.createContext<boolean>(false);
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 [ value, setValue ] = useState<ConnectProperties[T] | V>(() => {
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));
return value;
return [value, setValue];
}
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 events = useContext(EventContext);
const address = useProperty("address", undefined);
const [address, setAddress] = useProperty("address", undefined);
const valid = usePropertyValid("address", true);
const newTab = useContext(ConnectDefaultNewTabContext);
@ -64,16 +64,25 @@ const InputServerAddress = () => {
invalid={valid ? undefined : <Translatable>Please enter a valid server address</Translatable>}
onInput={value => events.fire("action_set_address", { address: value, validate: false })}
onBlur={() => events.fire("action_set_address", { address: address?.currentAddress, validate: true })}
onEnter={() => events.fire("action_connect", { newTab })}
onInput={value => {
setAddress({ currentAddress: value, defaultAddress: address.defaultAddress });
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 events = useContext(EventContext);
const password = useProperty("password", undefined);
const [password, setPassword] = useProperty("password", undefined);
return (
<FlatInputField
@ -83,14 +92,22 @@ const InputServerPassword = () => {
type={"password"}
label={<Translatable>Server password</Translatable>}
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 events = useContext(EventContext);
const nickname = useProperty("nickname", undefined);
const [nickname, setNickname] = useProperty("nickname", undefined);
const valid = usePropertyValid("nickname", true);
return (
@ -101,15 +118,18 @@ const InputNickname = () => {
label={<Translatable>Nickname</Translatable>}
labelType={"static"}
invalid={valid ? undefined : <Translatable>Nickname too short or too long</Translatable>}
onInput={value => events.fire("action_set_nickname", { nickname: value, validate: false })}
onBlur={() => events.fire("action_set_nickname", { nickname: nickname?.currentNickname, validate: true })}
onInput={value => {
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 events = useContext(EventContext);
const profiles = useProperty("profiles", undefined);
const [profiles] = useProperty("profiles", undefined);
const selectedProfile = profiles?.profiles.find(profile => profile.id === profiles?.selected);
let invalidMarker;
@ -343,7 +363,7 @@ const HistoryTableEntry = React.memo((props: { entry: ConnectHistoryEntry, selec
});
const HistoryTable = () => {
const history = useProperty("history", undefined);
const [history] = useProperty("history", undefined);
let body;
if(history) {