| 
									
										
										
										
											2023-03-12 16:00:57 +01:00
										 |  |  | // GoToSocial | 
					
						
							|  |  |  | // Copyright (C) GoToSocial Authors admin@gotosocial.org | 
					
						
							|  |  |  | // SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | // GNU Affero General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | package timeline | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | // GrabFunction is used by a Timeline to grab more items to index. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // It should be provided to NewTimeline when the caller is creating a timeline | 
					
						
							|  |  |  | // (of statuses, notifications, etc). | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2022-09-28 18:30:40 +01:00
										 |  |  | //	timelineAccountID: the owner of the timeline | 
					
						
							|  |  |  | //	maxID: the maximum item ID desired. | 
					
						
							|  |  |  | //	sinceID: the minimum item ID desired. | 
					
						
							|  |  |  | //	minID: see sinceID | 
					
						
							|  |  |  | //	limit: the maximum amount of items to be returned | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | // | 
					
						
							|  |  |  | // If an error is returned, the timeline will stop processing whatever request called GrabFunction, | 
					
						
							|  |  |  | // and return the error. If no error is returned, but stop = true, this indicates to the caller of GrabFunction | 
					
						
							|  |  |  | // that there are no more items to return, and processing should continue with the items already grabbed. | 
					
						
							|  |  |  | type GrabFunction func(ctx context.Context, timelineAccountID string, maxID string, sinceID string, minID string, limit int) (items []Timelineable, stop bool, err error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // FilterFunction is used by a Timeline to filter whether or not a grabbed item should be indexed. | 
					
						
							|  |  |  | type FilterFunction func(ctx context.Context, timelineAccountID string, item Timelineable) (shouldIndex bool, err error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // PrepareFunction converts a Timelineable into a Preparable. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // For example, this might result in the converstion of a *gtsmodel.Status with the given itemID into a serializable *apimodel.Status. | 
					
						
							|  |  |  | type PrepareFunction func(ctx context.Context, timelineAccountID string, itemID string) (Preparable, error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SkipInsertFunction indicates whether a new item about to be inserted in the prepared list should be skipped, | 
					
						
							|  |  |  | // based on the item itself, the next item in the timeline, and the depth at which nextItem has been found in the list. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This will be called for every item found while iterating through a timeline, so callers should be very careful | 
					
						
							|  |  |  | // not to do anything expensive here. | 
					
						
							|  |  |  | type SkipInsertFunction func(ctx context.Context, | 
					
						
							|  |  |  | 	newItemID string, | 
					
						
							|  |  |  | 	newItemAccountID string, | 
					
						
							|  |  |  | 	newItemBoostOfID string, | 
					
						
							|  |  |  | 	newItemBoostOfAccountID string, | 
					
						
							|  |  |  | 	nextItemID string, | 
					
						
							|  |  |  | 	nextItemAccountID string, | 
					
						
							|  |  |  | 	nextItemBoostOfID string, | 
					
						
							|  |  |  | 	nextItemBoostOfAccountID string, | 
					
						
							|  |  |  | 	depth int) (bool, error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Timeline represents a timeline for one account, and contains indexed and prepared items. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | type Timeline interface { | 
					
						
							|  |  |  | 	/* | 
					
						
							|  |  |  | 		RETRIEVAL FUNCTIONS | 
					
						
							|  |  |  | 	*/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// Get returns an amount of prepared items with the given parameters. | 
					
						
							| 
									
										
										
										
											2021-08-15 18:43:08 +02:00
										 |  |  | 	// If prepareNext is true, then the next predicted query will be prepared already in a goroutine, | 
					
						
							|  |  |  | 	// to make the next call to Get faster. | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	Get(ctx context.Context, amount int, maxID string, sinceID string, minID string, prepareNext bool) ([]Preparable, error) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 		INDEXING + PREPARATION FUNCTIONS | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	*/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// IndexOne puts a item into the timeline at the appropriate place according to its 'createdAt' property. | 
					
						
							| 
									
										
										
										
											2021-06-19 11:18:55 +02:00
										 |  |  | 	// | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// The returned bool indicates whether or not the item was actually inserted into the timeline. This will be false | 
					
						
							|  |  |  | 	// if the item is a boost and the original item or another boost of it already exists < boostReinsertionDepth back in the timeline. | 
					
						
							|  |  |  | 	IndexOne(ctx context.Context, itemID string, boostOfID string, accountID string, boostOfAccountID string) (bool, error) | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 	// IndexAndPrepareOne puts a item into the timeline at the appropriate place according to its 'createdAt' property, | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	// and then immediately prepares it. | 
					
						
							| 
									
										
										
										
											2021-06-19 11:18:55 +02:00
										 |  |  | 	// | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// The returned bool indicates whether or not the item was actually inserted into the timeline. This will be false | 
					
						
							|  |  |  | 	// if the item is a boost and the original item or another boost of it already exists < boostReinsertionDepth back in the timeline. | 
					
						
							|  |  |  | 	IndexAndPrepareOne(ctx context.Context, itemID string, boostOfID string, accountID string, boostOfAccountID string) (bool, error) | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 	// PrepareXFromTop instructs the timeline to prepare x amount of items from the top of the timeline, useful during init. | 
					
						
							|  |  |  | 	PrepareFromTop(ctx context.Context, amount int) error | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* | 
					
						
							|  |  |  | 		INFO FUNCTIONS | 
					
						
							|  |  |  | 	*/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// ActualPostIndexLength returns the actual length of the item index at this point in time. | 
					
						
							|  |  |  | 	ItemIndexLength(ctx context.Context) int | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 	// OldestIndexedItemID returns the id of the rearmost (ie., the oldest) indexed item, or an error if something goes wrong. | 
					
						
							|  |  |  | 	// If nothing goes wrong but there's no oldest item, an empty string will be returned so make sure to check for this. | 
					
						
							|  |  |  | 	OldestIndexedItemID(ctx context.Context) (string, error) | 
					
						
							|  |  |  | 	// NewestIndexedItemID returns the id of the frontmost (ie., the newest) indexed item, or an error if something goes wrong. | 
					
						
							|  |  |  | 	// If nothing goes wrong but there's no newest item, an empty string will be returned so make sure to check for this. | 
					
						
							|  |  |  | 	NewestIndexedItemID(ctx context.Context) (string, error) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* | 
					
						
							|  |  |  | 		UTILITY FUNCTIONS | 
					
						
							|  |  |  | 	*/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 	// LastGot returns the time that Get was last called. | 
					
						
							|  |  |  | 	LastGot() time.Time | 
					
						
							|  |  |  | 	// Prune prunes preparedItems and indexedItems in this timeline to the desired lengths. | 
					
						
							|  |  |  | 	// This will be a no-op if the lengths are already < the desired values. | 
					
						
							|  |  |  | 	// Prune acquires a lock on the timeline before pruning. | 
					
						
							|  |  |  | 	// The return value is the combined total of items pruned from preparedItems and indexedItems. | 
					
						
							|  |  |  | 	Prune(desiredPreparedItemsLength int, desiredIndexedItemsLength int) int | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// Remove removes a item from both the index and prepared items. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	// | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// If a item has multiple entries in a timeline, they will all be removed. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	// | 
					
						
							|  |  |  | 	// The returned int indicates the amount of entries that were removed. | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	Remove(ctx context.Context, itemID string) (int, error) | 
					
						
							|  |  |  | 	// RemoveAllBy removes all items by the given accountID, from both the index and prepared items. | 
					
						
							| 
									
										
										
										
											2021-07-11 16:22:21 +02:00
										 |  |  | 	// | 
					
						
							|  |  |  | 	// The returned int indicates the amount of entries that were removed. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	RemoveAllBy(ctx context.Context, accountID string) (int, error) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // timeline fulfils the Timeline interface | 
					
						
							|  |  |  | type timeline struct { | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 	indexedItems    *indexedItems | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	preparedItems   *preparedItems | 
					
						
							|  |  |  | 	grabFunction    GrabFunction | 
					
						
							|  |  |  | 	filterFunction  FilterFunction | 
					
						
							|  |  |  | 	prepareFunction PrepareFunction | 
					
						
							|  |  |  | 	accountID       string | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 	lastGot         time.Time | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	sync.Mutex | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewTimeline returns a new Timeline for the given account ID | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | func NewTimeline( | 
					
						
							|  |  |  | 	ctx context.Context, | 
					
						
							|  |  |  | 	timelineAccountID string, | 
					
						
							|  |  |  | 	grabFunction GrabFunction, | 
					
						
							|  |  |  | 	filterFunction FilterFunction, | 
					
						
							|  |  |  | 	prepareFunction PrepareFunction, | 
					
						
							| 
									
										
										
										
											2022-12-08 17:35:14 +00:00
										 |  |  | 	skipInsertFunction SkipInsertFunction, | 
					
						
							|  |  |  | ) (Timeline, error) { | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	return &timeline{ | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 		indexedItems: &indexedItems{ | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 			skipInsert: skipInsertFunction, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		preparedItems: &preparedItems{ | 
					
						
							|  |  |  | 			skipInsert: skipInsertFunction, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		grabFunction:    grabFunction, | 
					
						
							|  |  |  | 		filterFunction:  filterFunction, | 
					
						
							|  |  |  | 		prepareFunction: prepareFunction, | 
					
						
							|  |  |  | 		accountID:       timelineAccountID, | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 		lastGot:         time.Time{}, | 
					
						
							| 
									
										
										
										
											2021-06-23 18:42:20 +02:00
										 |  |  | 	}, nil | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } |