using decePubClient.Models.Mastodon; using decePubClient.Models.Types; namespace decePubClient.Models { /// /// The persona's profile as Settings edits it, which is what update_credentials takes: the words as written (its /// account's Source), the flags, the posting defaults, and a new avatar or header when one was picked. Fields always /// holds FieldCount rows; a row without a name is dropped. /// public class ProfileForm { public const int FieldCount = 4; public const int DisplayNameLength = 30; public const int NoteLength = 500; public const int FieldLength = 255; public string DisplayName { get; set; } public string Note { get; set; } public bool Locked { get; set; } public bool Bot { get; set; } public bool Discoverable { get; set; } public bool Indexable { get; set; } public MessageType Privacy { get; set; } = MessageType.Public; public bool Sensitive { get; set; } public string Language { get; set; } public QuotePolicy QuotePolicy { get; set; } = QuotePolicy.Public; public List Fields { get; set; } = []; public UploadMedia Avatar { get; set; } public UploadMedia Header { get; set; } public static ProfileForm Of(Account account) { var source = account.Source ?? new(); var fields = source.Fields.Count > 0 ? source.Fields : account.Fields; return new() { DisplayName = account.DisplayName, Note = source.Note, Locked = account.Locked, Bot = account.Bot, Discoverable = account.Discoverable == true, Indexable = account.Indexable == true, Privacy = source.Privacy switch { "private" => MessageType.FollowersOnly, "unlisted" => MessageType.Unlisted, _ => MessageType.Public }, Sensitive = source.Sensitive, Language = source.Language, QuotePolicy = source.QuotePolicy switch { "followers" => QuotePolicy.Followers, "nobody" => QuotePolicy.Nobody, _ => QuotePolicy.Public }, Fields = [.. fields.Take(FieldCount).Select(f => new AccountField { Name = f.Name, Value = f.Value }), .. Enumerable.Range(0, Math.Max(0, FieldCount - fields.Count)).Select(_ => new AccountField())] }; } /// The form as update_credentials reads it, with the pictures as files. public MultipartFormDataContent Content() { var content = new MultipartFormDataContent { { new StringContent(DisplayName?.Trim() ?? ""), "display_name" }, { new StringContent(Note ?? ""), "note" }, { new StringContent(Flag(Locked)), "locked" }, { new StringContent(Flag(Bot)), "bot" }, { new StringContent(Flag(Discoverable)), "discoverable" }, { new StringContent(Flag(Indexable)), "indexable" }, { new StringContent(Privacy switch { MessageType.FollowersOnly => "private", MessageType.Unlisted => "unlisted", _ => "public" }), "source[privacy]" }, { new StringContent(Flag(Sensitive)), "source[sensitive]" }, { new StringContent(Language ?? ""), "source[language]" }, { new StringContent(QuotePolicy switch { QuotePolicy.Followers => "followers", QuotePolicy.Nobody => "nobody", _ => "public" }), "source[quote_policy]" } }; // every row is sent, so a row emptied here is removed there for (var i = 0; i < Fields.Count; i++) { content.Add(new StringContent(Fields[i].Name?.Trim() ?? ""), $"fields_attributes[{i}][name]"); content.Add(new StringContent(Fields[i].Value?.Trim() ?? ""), $"fields_attributes[{i}][value]"); } if (Avatar is not null) content.Add(Picture(Avatar), "avatar", Avatar.FileName); if (Header is not null) content.Add(Picture(Header), "header", Header.FileName); return content; } static string Flag(bool value) => value ? "true" : "false"; static ByteArrayContent Picture(UploadMedia media) { var picture = new ByteArrayContent(media.Blob); picture.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse(media.ContentType); return picture; } } }