From 0aadc2db2a42fc99538fbbb096b84b209b9ccd68 Mon Sep 17 00:00:00 2001 From: tobi <31960611+tsmethurst@users.noreply.github.com> Date: Wed, 17 Jul 2024 16:46:52 +0200 Subject: [feature] Allow users to set default interaction policies per status visibility (#3108) * [feature] Allow users to set default interaction policies * use vars for default policies * avoid some code repetition * unfuck form binding * avoid bonkers loop * beep boop * put policyValsToAPIPolicyVals in separate function * don't bother with slices.Grow * oops --- internal/typeutils/internaltofrontend.go | 123 +++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) (limited to 'internal/typeutils/internaltofrontend.go') diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index d24ae3ea5..6350f3269 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -1234,6 +1234,20 @@ func (c *Converter) baseStatusToFrontend( log.Errorf(ctx, "error converting status emojis: %v", err) } + // Take status's interaction policy, or + // fall back to default for its visibility. + var p *gtsmodel.InteractionPolicy + if s.InteractionPolicy != nil { + p = s.InteractionPolicy + } else { + p = gtsmodel.DefaultInteractionPolicyFor(s.Visibility) + } + + apiInteractionPolicy, err := c.InteractionPolicyToAPIInteractionPolicy(ctx, p, s, requestingAccount) + if err != nil { + return nil, gtserror.Newf("error converting interaction policy: %w", err) + } + apiStatus := &apimodel.Status{ ID: s.ID, CreatedAt: util.FormatISO8601(s.CreatedAt), @@ -1258,6 +1272,7 @@ func (c *Converter) baseStatusToFrontend( Emojis: apiEmojis, Card: nil, // TODO: implement cards Text: s.Text, + InteractionPolicy: *apiInteractionPolicy, } // Nullable fields. @@ -2256,3 +2271,111 @@ func (c *Converter) ThemesToAPIThemes(themes []*gtsmodel.Theme) []apimodel.Theme } return apiThemes } + +// Convert the given gtsmodel policy +// into an apimodel interaction policy. +// +// Provided status can be nil to convert a +// policy without a particular status in mind. +// +// RequestingAccount can also be nil for +// unauthorized requests (web, public api etc). +func (c *Converter) InteractionPolicyToAPIInteractionPolicy( + ctx context.Context, + policy *gtsmodel.InteractionPolicy, + _ *gtsmodel.Status, // Used in upcoming PR. + _ *gtsmodel.Account, // Used in upcoming PR. +) (*apimodel.InteractionPolicy, error) { + apiPolicy := &apimodel.InteractionPolicy{ + CanFavourite: apimodel.PolicyRules{ + Always: policyValsToAPIPolicyVals(policy.CanLike.Always), + WithApproval: policyValsToAPIPolicyVals(policy.CanLike.WithApproval), + }, + CanReply: apimodel.PolicyRules{ + Always: policyValsToAPIPolicyVals(policy.CanReply.Always), + WithApproval: policyValsToAPIPolicyVals(policy.CanReply.WithApproval), + }, + CanReblog: apimodel.PolicyRules{ + Always: policyValsToAPIPolicyVals(policy.CanAnnounce.Always), + WithApproval: policyValsToAPIPolicyVals(policy.CanAnnounce.WithApproval), + }, + } + + return apiPolicy, nil +} + +func policyValsToAPIPolicyVals(vals gtsmodel.PolicyValues) []apimodel.PolicyValue { + + var ( + valsLen = len(vals) + + // Use a map to deduplicate added vals as we go. + addedVals = make(map[apimodel.PolicyValue]struct{}, valsLen) + + // Vals we'll be returning. + apiVals = make([]apimodel.PolicyValue, 0, valsLen) + ) + + for _, policyVal := range vals { + switch policyVal { + + case gtsmodel.PolicyValueAuthor: + // Author can do this. + newVal := apimodel.PolicyValueAuthor + if _, added := addedVals[newVal]; !added { + apiVals = append(apiVals, newVal) + addedVals[newVal] = struct{}{} + } + + case gtsmodel.PolicyValueMentioned: + // Mentioned can do this. + newVal := apimodel.PolicyValueMentioned + if _, added := addedVals[newVal]; !added { + apiVals = append(apiVals, newVal) + addedVals[newVal] = struct{}{} + } + + case gtsmodel.PolicyValueMutuals: + // Mutuals can do this. + newVal := apimodel.PolicyValueMutuals + if _, added := addedVals[newVal]; !added { + apiVals = append(apiVals, newVal) + addedVals[newVal] = struct{}{} + } + + case gtsmodel.PolicyValueFollowing: + // Following can do this. + newVal := apimodel.PolicyValueFollowing + if _, added := addedVals[newVal]; !added { + apiVals = append(apiVals, newVal) + addedVals[newVal] = struct{}{} + } + + case gtsmodel.PolicyValueFollowers: + // Followers can do this. + newVal := apimodel.PolicyValueFollowers + if _, added := addedVals[newVal]; !added { + apiVals = append(apiVals, newVal) + addedVals[newVal] = struct{}{} + } + + case gtsmodel.PolicyValuePublic: + // Public can do this. + newVal := apimodel.PolicyValuePublic + if _, added := addedVals[newVal]; !added { + apiVals = append(apiVals, newVal) + addedVals[newVal] = struct{}{} + } + + default: + // Specific URI of ActivityPub Actor. + newVal := apimodel.PolicyValue(policyVal) + if _, added := addedVals[newVal]; !added { + apiVals = append(apiVals, newVal) + addedVals[newVal] = struct{}{} + } + } + } + + return apiVals +} -- cgit v1.2.3