using Invercargill; using Invercargill.DataStructures; using Spry; using Spry.Authentication; using Statum; using ClankerSaddle.Data; using ClankerSaddle.Services; namespace ClankerSaddle { /** A stack row on the stacks page, with its member names. */ public class StackRowView : Object { public int64 id { get; set; } public string name { get; set; default = ""; } public string summary { get; set; default = ""; } public string created_display { get; set; default = ""; } public int64 member_count { get; set; } } /** Public data of the `stacks` slot. */ public class StacksPublic : Object { public Statum.Model.ActionDto create { get; set; } } /** * Entrypoint for /stacks (goal 8 task 2 B): every stack with its * member count and a link to its page, plus the create form. A * stack is a named bundle of reference projects applied one-click * to a project's or a goal's link set. */ public class StacksEntrypoint : StatumEntrypoint { private StackService stack_service = Inversion.inject(); private ProjectService project_service = Inversion.inject(); private UserService users = Inversion.inject(); public override async DirectiveBuilder handle() throws GLib.Error { var guard = yield SpryAuth.require_login(directives(), request, users); if (guard != null) { return (!)guard; } var rows = new Vector(); foreach (var stack in yield stack_service.list_stacks()) { rows.add(new StackRowView() { id = stack.id, name = stack.name, summary = stack.summary ?? "", created_display = format_stamp(stack.created_at), member_count = (yield stack_service.list_members(stack.id)).length }); } var pub = new StacksPublic(); pub.create = action_registry.author(); var props = (PropertyDictionary) GObjectMapping.to_properties(pub); props["stacks"] = StateJson.list(rows.to_immutable_buffer()); var slot = state_service.new_slot(Scope.PAGE, new State() { type_name = "stacks", public_data = props, private_data = new PropertyDictionary() }); return directives().set(slot); } } /** A member project of a stack, with its remove action. */ public class StackMemberView : Object { public int64 member_id { get; set; } public int64 project_id { get; set; } public string name { get; set; default = ""; } public string git_url { get; set; default = ""; } public string relationship { get; set; default = ""; } public Statum.Model.ActionDto? remove { get; set; } } /** A project option in the member-add select. */ public class StackProjectOptionView : Object { public int64 id { get; set; } public string name { get; set; default = ""; } } /** Public data of the `stack` slot. */ public class StackDetailPublic : Object { public bool exists { get; set; } public int64 id { get; set; } public string name { get; set; default = ""; } public string summary { get; set; default = ""; } public string created_display { get; set; default = ""; } public Statum.Model.ActionDto? update { get; set; } public Statum.Model.ActionDto? add_member { get; set; } public Statum.Model.ActionDto? delete { get; set; } } /** * Entrypoint for /stacks/{stack_id}: the stack's editable card, * its member projects with relationship badges and remove actions, * and the add-member form (every project, relationship select). */ public class StackDetailEntrypoint : StatumEntrypoint { private StackService stack_service = Inversion.inject(); private ProjectService project_service = Inversion.inject(); private UserService users = Inversion.inject(); public override async DirectiveBuilder handle() throws GLib.Error { var guard = yield SpryAuth.require_login(directives(), request, users); if (guard != null) { return (!)guard; } int64 stack_id = int64.parse(request.route("stack_id") ?? "0"); var stack = yield stack_service.get_stack(stack_id); if (stack == null) { return directives().set_typed("stack", Scope.PAGE, new StackDetailPublic() { exists = false }); } var sealed = new StackActionPrivate() { stack_id = stack_id }; var pub = new StackDetailPublic() { exists = true, id = ((!)stack).id, name = ((!)stack).name, summary = ((!)stack).summary ?? "", created_display = format_stamp(((!)stack).created_at), update = action_registry.author_private(sealed), add_member = action_registry.author_private(sealed), delete = action_registry.author_private(sealed) }; var members = new Vector(); foreach (var member in yield stack_service.list_members(stack_id)) { var project = yield project_service.get_project(member.project_id); if (project == null) { continue; } members.add(new StackMemberView() { member_id = member.id, project_id = member.project_id, name = ((!)project).name, git_url = ((!)project).git_url, relationship = member.relationship, remove = action_registry.author_private( new StackMemberActionPrivate() { stack_id = stack_id, member_id = member.id }) }); } var options = new Vector(); foreach (var project in yield project_service.list_projects()) { options.add(new StackProjectOptionView() { id = project.id, name = project.name }); } var props = (PropertyDictionary) GObjectMapping.to_properties(pub); props["members"] = StateJson.list(members.to_immutable_buffer()); props["options"] = StateJson.list(options.to_immutable_buffer()); var slot = state_service.new_slot(Scope.PAGE, new State() { type_name = "stack", public_data = props, private_data = new PropertyDictionary() }); return directives().set(slot); } } }