OverviewFilesCommitsIssuesDocsReleasesPeers

godot-keet / addons / keet_social / keet_identity.gd

239 lines · gd ref:
class_name KeetIdentity
extends Node
## Identity management for keet-daemon via UDS.
## Full lifecycle: setup, recovery, username, display name, lookups.

signal identity_loaded(identity: Dictionary)
signal name_changed(name: String)
signal member_found(member: Dictionary)
signal recovery_phrase_received(phrase: String)
signal setup_step_completed(step: String)
signal username_registered(username: String)
signal error_occurred(message: String)

var client: KeetUdsClient


# --- Identity info ---

func get_identity(callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("identity", {}, func(result, err):
		if err != null:
			error_occurred.emit("get_identity: %s" % str(err))
			# Still call back with empty dict so vault check can proceed
			if callback.is_valid():
				callback.call({})
			return
		var ident: Dictionary = result if result is Dictionary else {}
		identity_loaded.emit(ident)
		if callback.is_valid():
			callback.call(ident)
	)


func get_status(callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("status", {}, func(result, err):
		if err != null:
			error_occurred.emit("get_status: %s" % str(err))
			return
		if callback.is_valid():
			callback.call(result)
	)


# --- Identity setup (first-run flow) ---
# Order: confirm → get recovery phrase → store phrase → set name → register username

func confirm_identity(callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("identity.confirm", {}, func(result, err):
		if err != null:
			error_occurred.emit("confirm: %s" % str(err))
			return
		setup_step_completed.emit("confirm")
		if callback.is_valid():
			callback.call(result)
	)


func get_recovery_phrase(callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("identity.recovery", {}, func(result, err):
		if err != null:
			error_occurred.emit("recovery: %s" % str(err))
			return
		var phrase: String = ""
		if result is Dictionary:
			phrase = result.get("phrase", "")
		recovery_phrase_received.emit(phrase)
		if callback.is_valid():
			callback.call(phrase)
	)


func store_recovery_phrase(phrase: String, callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("identity.store", {"phrase": phrase}, func(result, err):
		if err != null:
			error_occurred.emit("store: %s" % str(err))
			return
		setup_step_completed.emit("store")
		if callback.is_valid():
			callback.call(result)
	)


func restore_identity(phrase: String, callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("identity.restore", {"phrase": phrase}, func(result, err):
		if err != null:
			error_occurred.emit("restore: %s" % str(err))
			return
		setup_step_completed.emit("restore")
		if callback.is_valid():
			callback.call(result)
	)


# --- Username & display name ---

func set_display_name(display_name: String, callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("identity.setName", {"name": display_name}, func(result, err):
		if err != null:
			error_occurred.emit("set_name: %s" % str(err))
			return
		name_changed.emit(display_name)
		if callback.is_valid():
			callback.call(result)
	)


func register_username(username: String, callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("identity.register", {"username": username}, func(result, err):
		if err != null:
			error_occurred.emit("register: %s" % str(err))
			return
		username_registered.emit(username)
		setup_step_completed.emit("register")
		if callback.is_valid():
			callback.call(result)
	)


func check_username(username: String, callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("identity.checkName", {"username": username}, func(result, err):
		if err != null:
			error_occurred.emit("check_username: %s" % str(err))
			return
		if callback.is_valid():
			callback.call(result)
	)


# --- Vault (encrypted mnemonic) ---

func vault_status(callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("vault.status", {}, func(result, err):
		if err != null:
			error_occurred.emit("vault_status: %s" % str(err))
			if callback.is_valid():
				callback.call({})
			return
		if callback.is_valid():
			callback.call(result if result is Dictionary else {})
	)


func vault_setup(password: String, callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("vault.setup", {"password": password}, func(result, err):
		if err != null:
			error_occurred.emit("vault_setup: %s" % str(err))
			if callback.is_valid():
				callback.call({"ok": false, "error": str(err)})
			return
		setup_step_completed.emit("vault_setup")
		if callback.is_valid():
			callback.call(result if result is Dictionary else {"ok": true})
	)


func vault_unlock(password: String, callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("vault.unlock", {"password": password}, func(result, err):
		if err != null:
			error_occurred.emit("vault_unlock: %s" % str(err))
			if callback.is_valid():
				callback.call({"ok": false, "error": str(err)})
			return
		setup_step_completed.emit("vault_unlock")
		if callback.is_valid():
			callback.call(result if result is Dictionary else {"ok": true})
	)


func vault_import(phrase: String, password: String, callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("vault.import", {"phrase": phrase, "password": password}, func(result, err):
		if err != null:
			error_occurred.emit("vault_import: %s" % str(err))
			if callback.is_valid():
				callback.call({"ok": false, "error": str(err)})
			return
		setup_step_completed.emit("vault_import")
		if callback.is_valid():
			callback.call(result if result is Dictionary else {"ok": true})
	)


func vault_export(password: String, callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("vault.export", {"password": password}, func(result, err):
		if err != null:
			error_occurred.emit("vault_export: %s" % str(err))
			if callback.is_valid():
				callback.call({"ok": false, "error": str(err)})
			return
		if callback.is_valid():
			callback.call(result if result is Dictionary else {})
	)


# --- Lookups ---

func lookup_user(username: String, callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("identity.lookup", {"username": username}, func(result, err):
		if err != null:
			error_occurred.emit("lookup_user: %s" % str(err))
			return
		var member: Dictionary = result if result is Dictionary else {}
		member_found.emit(member)
		if callback.is_valid():
			callback.call(member)
	)


func lookup_member(room_id: String, member_id: String, callback: Callable = Callable()) -> void:
	_require_client()
	client.call_rpc("identity.lookupMember", {"roomId": room_id, "memberId": member_id}, func(result, err):
		if err != null:
			error_occurred.emit("lookup_member: %s" % str(err))
			return
		var member: Dictionary = result if result is Dictionary else {}
		member_found.emit(member)
		if callback.is_valid():
			callback.call(member)
	)


func _require_client() -> void:
	if client == null:
		push_error("KeetIdentity: client not set")