some more hacking away at the timeline code phew

This commit is contained in:
tsmethurst 2021-06-05 18:51:14 +02:00
parent 96048d7f07
commit 86fd23ccd9
8 changed files with 49 additions and 45 deletions

View File

@ -244,10 +244,6 @@ func (ps *postgresService) GetWhere(where []db.Where, i interface{}) error {
return nil return nil
} }
// func (ps *postgresService) GetWhereMany(i interface{}, where ...model.Where) error {
// return nil
// }
func (ps *postgresService) GetAll(i interface{}) error { func (ps *postgresService) GetAll(i interface{}) error {
if err := ps.conn.Model(i).Select(); err != nil { if err := ps.conn.Model(i).Select(); err != nil {
if err == pg.ErrNoRows { if err == pg.ErrNoRows {
@ -1257,6 +1253,8 @@ func (ps *postgresService) GetNotificationsForAccount(accountID string, limit in
CONVERSION FUNCTIONS CONVERSION FUNCTIONS
*/ */
// TODO: move these to the type converter, it's bananas that they're here and not there
func (ps *postgresService) MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*gtsmodel.Mention, error) { func (ps *postgresService) MentionStringsToMentions(targetAccounts []string, originAccountID string, statusID string) ([]*gtsmodel.Mention, error) {
ogAccount := &gtsmodel.Account{} ogAccount := &gtsmodel.Account{}
if err := ps.conn.Model(ogAccount).Where("id = ?", originAccountID).Select(); err != nil { if err := ps.conn.Model(ogAccount).Where("id = ?", originAccountID).Select(); err != nil {
@ -1341,10 +1339,11 @@ func (ps *postgresService) TagStringsToTags(tags []string, originAccountID strin
tag := &gtsmodel.Tag{} tag := &gtsmodel.Tag{}
// we can use selectorinsert here to create the new tag if it doesn't exist already // we can use selectorinsert here to create the new tag if it doesn't exist already
// inserted will be true if this is a new tag we just created // inserted will be true if this is a new tag we just created
if err := ps.conn.Model(tag).Where("name = ?", t).Select(); err != nil { if err := ps.conn.Model(tag).Where("LOWER(?) = LOWER(?)", pg.Ident("name"), t).Select(); err != nil {
if err == pg.ErrNoRows { if err == pg.ErrNoRows {
// tag doesn't exist yet so populate it // tag doesn't exist yet so populate it
tag.ID = uuid.NewString() tag.ID = uuid.NewString()
tag.URL = fmt.Sprintf("%s://%s/tags/%s", ps.config.Protocol, ps.config.Host, t)
tag.Name = t tag.Name = t
tag.FirstSeenFromAccountID = originAccountID tag.FirstSeenFromAccountID = originAccountID
tag.CreatedAt = time.Now() tag.CreatedAt = time.Now()

View File

@ -21,7 +21,6 @@ func (p *processor) Create(account *gtsmodel.Account, application *gtsmodel.Appl
ID: thisStatusID, ID: thisStatusID,
URI: thisStatusURI, URI: thisStatusURI,
URL: thisStatusURL, URL: thisStatusURL,
Content: util.HTMLFormat(form.Status),
CreatedAt: time.Now(), CreatedAt: time.Now(),
UpdatedAt: time.Now(), UpdatedAt: time.Now(),
Local: true, Local: true,

View File

@ -248,6 +248,12 @@ func (p *processor) processContent(form *apimodel.AdvancedStatusCreateForm, acco
} }
} }
// format tags nicely
for _, tag := range status.GTSTags {
tagContent := fmt.Sprintf(`<a href="%s" class="mention hashtag" rel="tag">#<span>%s</span></a>`, tag.URL, tag.Name)
content = strings.ReplaceAll(content, fmt.Sprintf("#%s", tag.Name), tagContent)
}
// replace newlines with breaks // replace newlines with breaks
content = strings.ReplaceAll(content, "\n", "<br />") content = strings.ReplaceAll(content, "\n", "<br />")

View File

@ -143,8 +143,10 @@ func (m *manager) HomeTimeline(timelineAccountID string, maxID string, sinceID s
var err error var err error
var statuses []*apimodel.Status var statuses []*apimodel.Status
if maxID != "" { if maxID != "" && sinceID != "" {
statuses, err = t.GetXFromIDOnwards(limit, maxID) statuses, err = t.GetXBetweenID(limit, maxID, sinceID)
} else if maxID != "" {
statuses, err = t.GetXBehindID(limit, maxID)
} else if sinceID != "" { } else if sinceID != "" {
statuses, err = t.GetXBeforeID(limit, sinceID) statuses, err = t.GetXBeforeID(limit, sinceID)
} else { } else {

View File

@ -43,12 +43,21 @@ type Timeline interface {
// GetXFromTop returns x amount of posts from the top of the timeline, from newest to oldest. // GetXFromTop returns x amount of posts from the top of the timeline, from newest to oldest.
GetXFromTop(amount int) ([]*apimodel.Status, error) GetXFromTop(amount int) ([]*apimodel.Status, error)
// GetXFromIDOnwards returns x amount of posts from the given id onwards, from newest to oldest. // GetXBehindID returns x amount of posts from the given id onwards, from newest to oldest.
// This will include the status with the given ID. // This will NOT include the status with the given ID.
GetXFromIDOnwards(amount int, fromID string) ([]*apimodel.Status, error) //
// This corresponds to an api call to /timelines/home?max_id=WHATEVER
GetXBehindID(amount int, fromID string) ([]*apimodel.Status, error)
// GetXBeforeID returns x amount of posts up to the given id, from newest to oldest. // GetXBeforeID returns x amount of posts up to the given id, from newest to oldest.
// This will NOT include the status with the given ID. // This will NOT include the status with the given ID.
//
// This corresponds to an api call to /timelines/home?since_id=WHATEVER
GetXBeforeID(amount int, sinceID string) ([]*apimodel.Status, error) GetXBeforeID(amount int, sinceID string) ([]*apimodel.Status, error)
// GetXBetweenID returns x amount of posts from the given maxID, up to the given id, from newest to oldest.
// This will NOT include the status with the given IDs.
//
// This corresponds to an api call to /timelines/home?since_id=WHATEVER&max_id=WHATEVER_ELSE
GetXBetweenID(amount int, maxID string, sinceID string) ([]*apimodel.Status, error)
/* /*
INDEXING FUNCTIONS INDEXING FUNCTIONS
@ -126,10 +135,11 @@ func (t *timeline) PrepareXFromPosition(amount int, desiredPosition int) error {
if !preparing { if !preparing {
// we haven't hit the position we need to prepare from yet // we haven't hit the position we need to prepare from yet
position = position + 1
if position == desiredPosition { if position == desiredPosition {
preparing = true preparing = true
continue
} }
position = position + 1
} else { } else {
if err := t.prepare(entry.statusID); err != nil { if err := t.prepare(entry.statusID); err != nil {
return fmt.Errorf("PrepareXFromTop: error preparing status with id %s: %s", entry.statusID, err) return fmt.Errorf("PrepareXFromTop: error preparing status with id %s: %s", entry.statusID, err)
@ -205,7 +215,7 @@ func (t *timeline) GetXFromTop(amount int) ([]*apimodel.Status, error) {
return statuses, nil return statuses, nil
} }
func (t *timeline) GetXFromIDOnwards(amount int, fromID string) ([]*apimodel.Status, error) { func (t *timeline) GetXBehindID(amount int, behindID string) ([]*apimodel.Status, error) {
// make a slice of statuses with the length we need to return // make a slice of statuses with the length we need to return
statuses := make([]*apimodel.Status, 0, amount) statuses := make([]*apimodel.Status, 0, amount)
@ -221,7 +231,7 @@ func (t *timeline) GetXFromIDOnwards(amount int, fromID string) ([]*apimodel.Sta
if !ok { if !ok {
return nil, errors.New("GetXBehindID: could not parse e as a preparedPostsEntry") return nil, errors.New("GetXBehindID: could not parse e as a preparedPostsEntry")
} }
if entry.statusID == fromID { if entry.statusID == behindID {
break break
} }
position = position + 1 position = position + 1
@ -245,12 +255,11 @@ func (t *timeline) GetXFromIDOnwards(amount int, fromID string) ([]*apimodel.Sta
if !serving { if !serving {
// start serving if we've hit the id we're looking for // start serving if we've hit the id we're looking for
if entry.statusID == fromID { if entry.statusID == behindID {
serving = true serving = true
continue
} }
} } else {
if serving {
// serve up to the amount requested // serve up to the amount requested
statuses = append(statuses, entry.prepared) statuses = append(statuses, entry.prepared)
served = served + 1 served = served + 1
@ -297,6 +306,16 @@ servloop:
return statuses, nil return statuses, nil
} }
func (t *timeline) GetXBetweenID(amount int, maxID string, sinceID string) ([]*apimodel.Status, error) {
// make a slice of statuses with the length we need to return
statuses := make([]*apimodel.Status, 0, amount)
// if there are no prepared posts, just return the empty slice
if t.preparedPosts.data == nil {
t.preparedPosts.data = &list.List{}
}
}
func (t *timeline) IndexOne(statusCreatedAt time.Time, statusID string) error { func (t *timeline) IndexOne(statusCreatedAt time.Time, statusID string) error {
t.Lock() t.Lock()
defer t.Unlock() defer t.Unlock()

View File

@ -56,7 +56,7 @@ func (c *converter) StatusToBoost(s *gtsmodel.Status, boostingAccount *gtsmodel.
Emojis: []string{}, Emojis: []string{},
// the below fields will be taken from the target status // the below fields will be taken from the target status
Content: util.HTMLFormat(s.Content), Content: s.Content,
ContentWarning: s.ContentWarning, ContentWarning: s.ContentWarning,
ActivityStreamsType: s.ActivityStreamsType, ActivityStreamsType: s.ActivityStreamsType,
Sensitive: s.Sensitive, Sensitive: s.Sensitive,

View File

@ -41,11 +41,11 @@ var (
mentionNameRegex = regexp.MustCompile(mentionNameRegexString) mentionNameRegex = regexp.MustCompile(mentionNameRegexString)
// mention regex can be played around with here: https://regex101.com/r/qwM9D3/1 // mention regex can be played around with here: https://regex101.com/r/qwM9D3/1
mentionFinderRegexString = `(?: |^|\W)?(@[a-zA-Z0-9_]+(?:@[a-zA-Z0-9_\-\.]+)?)(?:[^a-zA-Z0-9]|\W)` mentionFinderRegexString = `(?: |^|\W)(@[a-zA-Z0-9_]+(?:@[a-zA-Z0-9_\-\.]+)?)(?:[^a-zA-Z0-9]|\W|$)?`
mentionFinderRegex = regexp.MustCompile(mentionFinderRegexString) mentionFinderRegex = regexp.MustCompile(mentionFinderRegexString)
// hashtag regex can be played with here: https://regex101.com/r/Vhy8pg/1 // hashtag regex can be played with here: https://regex101.com/r/Vhy8pg/1
hashtagFinderRegexString = fmt.Sprintf(`(?: |^|\W)?#([a-zA-Z0-9]{1,%d})(?:\b|\r)`, maximumHashtagLength) hashtagFinderRegexString = fmt.Sprintf(`(?:\b)?#(\w{1,%d})(?:\b)`, maximumHashtagLength)
hashtagFinderRegex = regexp.MustCompile(hashtagFinderRegexString) hashtagFinderRegex = regexp.MustCompile(hashtagFinderRegexString)
// emoji shortcode regex can be played with here: https://regex101.com/r/zMDRaG/1 // emoji shortcode regex can be played with here: https://regex101.com/r/zMDRaG/1

View File

@ -35,7 +35,7 @@ func DeriveMentionsFromStatus(status string) []string {
for _, m := range mentionFinderRegex.FindAllStringSubmatch(status, -1) { for _, m := range mentionFinderRegex.FindAllStringSubmatch(status, -1) {
mentionedAccounts = append(mentionedAccounts, m[1]) mentionedAccounts = append(mentionedAccounts, m[1])
} }
return lower(unique(mentionedAccounts)) return unique(mentionedAccounts)
} }
// DeriveHashtagsFromStatus takes a plaintext (ie., not html-formatted) status, // DeriveHashtagsFromStatus takes a plaintext (ie., not html-formatted) status,
@ -47,7 +47,7 @@ func DeriveHashtagsFromStatus(status string) []string {
for _, m := range hashtagFinderRegex.FindAllStringSubmatch(status, -1) { for _, m := range hashtagFinderRegex.FindAllStringSubmatch(status, -1) {
tags = append(tags, m[1]) tags = append(tags, m[1])
} }
return lower(unique(tags)) return unique(tags)
} }
// DeriveEmojisFromStatus takes a plaintext (ie., not html-formatted) status, // DeriveEmojisFromStatus takes a plaintext (ie., not html-formatted) status,
@ -59,7 +59,7 @@ func DeriveEmojisFromStatus(status string) []string {
for _, m := range emojiFinderRegex.FindAllStringSubmatch(status, -1) { for _, m := range emojiFinderRegex.FindAllStringSubmatch(status, -1) {
emojis = append(emojis, m[1]) emojis = append(emojis, m[1])
} }
return lower(unique(emojis)) return unique(emojis)
} }
// ExtractMentionParts extracts the username test_user and the domain example.org // ExtractMentionParts extracts the username test_user and the domain example.org
@ -94,24 +94,3 @@ func unique(s []string) []string {
} }
return list return list
} }
// lower lowercases all strings in a given string slice
func lower(s []string) []string {
new := []string{}
for _, i := range s {
new = append(new, strings.ToLower(i))
}
return new
}
// HTMLFormat takes a plaintext formatted status string, and converts it into
// a nice HTML-formatted string.
//
// This includes:
// - Replacing line-breaks with <p>
// - Replacing URLs with hrefs.
// - Replacing mentions with links to that account's URL as stored in the database.
func HTMLFormat(status string) string {
// TODO: write proper HTML formatting logic for a status
return status
}