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
|
||||
* @param serverUniqueId
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue