╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Settings.zig ─────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
315 return; 316 } 317 switch (key) { 318 .enter => if (on_submit) return self.submitForm(allocator), 319 .mouse => |mouse| if (on_submit) { 320 if (inp.leftClickOn(root_focus, child.widget.submit_button.buttonId(), mouse)) return self.submitForm(allocator); 321 }, 322 else => {}, 323 } 324 try child.widget.input(allocator, key, root_focus); 325 } 326 327 fn formRadio(form: *wgt.Box(ui.Widget), name: []const u8) !*ui.widget.Radio { 328 for (form.children.values()) |*child| switch (child.widget) { 329 .radio => |*radio| if (std.mem.eql(u8, radio.name, name)) return radio, 330 else => {}, 331 }; 332 return error.MissingFormField; 333 } 334 335 fn formField(form: *wgt.Box(ui.Widget), name: []const u8) !*wgt.TextInput { 336 for (form.children.values()) |*child| switch (child.widget) { 337 .text_input => |*text_input| if (std.mem.eql(u8, text_input.options.name, name)) return text_input, 338 else => {}, 339 }; 340 return error.MissingFormField; 341 } 342 343 // update the repo and navigate to its new url. this is the terminal path; 344 // the web posts the form to the settings route. 345 fn submitForm(self: *Form, allocator: std.mem.Allocator) !void { 346 if (comptime wasm) return; 347 const io = self.session.io orelse return; 348 const users_dir = self.session.users_dir orelse return; 349 const actor = (try self.session.authorize(self.data.identity, .owner, null)) orelse return; 350 351 const name = try (try formField(self.formBox(), "name")).text(allocator); 352 defer allocator.free(name); 353 const description = try (try formField(self.formBox(), "description")).text(allocator); 354 defer allocator.free(description); 355 const access = std.meta.stringToEnum(evt.Repo.Access, (try formRadio(self.formBox(), "access")).selected()) orelse unreachable; 356 const discuss_role = labelRole((try formRadio(self.formBox(), "discuss_role")).selected()) catch unreachable; 357 const issue_role = labelRole((try formRadio(self.formBox(), "issue_role")).selected()) catch unreachable; 358 const patch_role = labelRole((try formRadio(self.formBox(), "patch_role")).selected()) catch unreachable; 359 360 const route = update(io, allocator, users_dir, actor, self.data.identity, name, description, access, discuss_role, issue_role, patch_role) catch |err| { 361 const failure = ui.Session.FormFeedback.RepoFailure.fromError(err) orelse return err; 362 const aa = self.session.arena.allocator(); 363 self.session.data.form_feedback = .{ .repo_settings = .{ .failure = failure, .fields = .{ 364 .name = try aa.dupe(u8, name), 365 .description = try aa.dupe(u8, description), 366 .access = access, 367 .discuss_role = discuss_role, 368 .issue_role = issue_role, 369 .patch_role = patch_role, 370 } } }; 371 return; 372 }; 373 try self.session.navigate(route); 374 } 375 376 fn formBox(self: *Form) *wgt.Box(ui.Widget) { 377 return &self.scroll.child.center.child.box; 378 } 379 380 pub fn clearGrid(self: *Form) void { 381 self.scroll.clearGrid(); 382 } 383 384 pub fn getGrid(self: Form) ?Grid { 385 return self.scroll.getGrid(); 386 } 387 388 pub fn getFocus(self: *Form) *Focus { 389 return self.scroll.getFocus(); 390 } 391 392 // up leaves the form from its first control 393 pub fn atTop(self: Form) bool { 394 const box = &self.scroll.child.center.child.box; 395 return box.focus.child_id == box.children.keys()[0]; 396 } 397 }; 398 399 // change the repo `identity` names as `actor`, its owner, returning its 400 // settings route under its new name 401 pub fn update( 402 io: std.Io, 403 allocator: std.mem.Allocator, 404 users_dir: []const u8, 405 actor: ui.Actor, 406 identity: []const u8, 407 name: []const u8, 408 description: []const u8, 409 access: evt.Repo.Access, 410 discuss_role: ?evt.Repo.Role, 411 issue_role: ?evt.Repo.Role, 412 patch_role: ?evt.Repo.Role, 413 ) !ui.RoutablePage { 414 const parsed = ui.RoutablePage.RepoIdentity.parse(identity) orelse return error.NotFound; 415 const owner_id = actor.repo_user_id orelse return error.NotFound; 416 try evt.updateRepo(io, allocator, users_dir, &owner_id, actor.author, parsed.name, name, description, access, discuss_role, issue_role, patch_role); 417 var buf: [ui.RoutablePage.repo_route_max_len]u8 = undefined; 418 const new_identity = std.fmt.bufPrint(&buf, "{s}:{s}", .{ parsed.owner, name }) catch return error.RouteTooLong; 419 return ui.RoutablePage.repoSettingsRoute(new_identity) orelse error.RouteTooLong; 420 } 421