gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

indexeditems.go (3389B)


      1 // GoToSocial
      2 // Copyright (C) GoToSocial Authors admin@gotosocial.org
      3 // SPDX-License-Identifier: AGPL-3.0-or-later
      4 //
      5 // This program is free software: you can redistribute it and/or modify
      6 // it under the terms of the GNU Affero General Public License as published by
      7 // the Free Software Foundation, either version 3 of the License, or
      8 // (at your option) any later version.
      9 //
     10 // This program is distributed in the hope that it will be useful,
     11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 // GNU Affero General Public License for more details.
     14 //
     15 // You should have received a copy of the GNU Affero General Public License
     16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18 package timeline
     19 
     20 import (
     21 	"container/list"
     22 	"context"
     23 
     24 	"github.com/superseriousbusiness/gotosocial/internal/gtserror"
     25 )
     26 
     27 type indexedItems struct {
     28 	data       *list.List
     29 	skipInsert SkipInsertFunction
     30 }
     31 
     32 type indexedItemsEntry struct {
     33 	itemID           string
     34 	boostOfID        string
     35 	accountID        string
     36 	boostOfAccountID string
     37 	prepared         Preparable
     38 }
     39 
     40 // WARNING: ONLY CALL THIS FUNCTION IF YOU ALREADY HAVE
     41 // A LOCK ON THE TIMELINE CONTAINING THIS INDEXEDITEMS!
     42 func (i *indexedItems) insertIndexed(ctx context.Context, newEntry *indexedItemsEntry) (bool, error) {
     43 	// Lazily init indexed items.
     44 	if i.data == nil {
     45 		i.data = &list.List{}
     46 		i.data.Init()
     47 	}
     48 
     49 	if i.data.Len() == 0 {
     50 		// We have no entries yet, meaning this is both the
     51 		// newest + oldest entry, so just put it in the front.
     52 		i.data.PushFront(newEntry)
     53 		return true, nil
     54 	}
     55 
     56 	var (
     57 		insertMark      *list.Element
     58 		currentPosition int
     59 	)
     60 
     61 	// We need to iterate through the index to make sure we put
     62 	// this item in the appropriate place according to its id.
     63 	// We also need to make sure we're not inserting a duplicate
     64 	// item -- this can happen sometimes and it's sucky UX.
     65 	for e := i.data.Front(); e != nil; e = e.Next() {
     66 		currentPosition++
     67 
     68 		currentEntry := e.Value.(*indexedItemsEntry) //nolint:forcetypeassert
     69 
     70 		// Check if we need to skip inserting this item based on
     71 		// the current item.
     72 		//
     73 		// For example, if the new item is a boost, and the current
     74 		// item is the original, we may not want to insert the boost
     75 		// if it would appear very shortly after the original.
     76 		if skip, err := i.skipInsert(
     77 			ctx,
     78 			newEntry.itemID,
     79 			newEntry.accountID,
     80 			newEntry.boostOfID,
     81 			newEntry.boostOfAccountID,
     82 			currentEntry.itemID,
     83 			currentEntry.accountID,
     84 			currentEntry.boostOfID,
     85 			currentEntry.boostOfAccountID,
     86 			currentPosition,
     87 		); err != nil {
     88 			return false, gtserror.Newf("error calling skipInsert: %w", err)
     89 		} else if skip {
     90 			// We don't need to insert this at all,
     91 			// so we can safely bail.
     92 			return false, nil
     93 		}
     94 
     95 		if insertMark != nil {
     96 			// We already found our mark.
     97 			continue
     98 		}
     99 
    100 		if currentEntry.itemID > newEntry.itemID {
    101 			// We're still in items newer than
    102 			// the one we're trying to insert.
    103 			continue
    104 		}
    105 
    106 		// We found our spot!
    107 		insertMark = e
    108 	}
    109 
    110 	if insertMark == nil {
    111 		// We looked through the whole timeline and didn't find
    112 		// a mark, so the new item is the oldest item we've seen;
    113 		// insert it at the back.
    114 		i.data.PushBack(newEntry)
    115 		return true, nil
    116 	}
    117 
    118 	i.data.InsertBefore(newEntry, insertMark)
    119 	return true, nil
    120 }