Migrate to new follows storage
This commit is contained in:
parent
99f5ae1b4f
commit
803d303420
@ -46,11 +46,9 @@ func (item *AnimeListItem) Edit(ctx aero.Context, key string, value reflect.Valu
|
||||
|
||||
// Broadcast event to all users so they can reload the activity page if needed.
|
||||
for receiver := range StreamUsers() {
|
||||
receiverIsFollowing := Contains(receiver.Follows().Items, user.ID)
|
||||
|
||||
receiver.BroadcastEvent(&aero.Event{
|
||||
Name: "activity",
|
||||
Data: receiverIsFollowing,
|
||||
Data: receiver.IsFollowing(user.ID),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
19
arn/User.go
19
arn/User.go
@ -20,17 +20,10 @@ import (
|
||||
gravatar "github.com/ungerik/go-gravatar"
|
||||
)
|
||||
|
||||
var setNickMutex sync.Mutex
|
||||
var setEmailMutex sync.Mutex
|
||||
|
||||
// Register data lists.
|
||||
func init() {
|
||||
DataLists["genders"] = []*Option{
|
||||
// &Option{"", "Prefer not to say"},
|
||||
{"male", "Male"},
|
||||
{"female", "Female"},
|
||||
}
|
||||
}
|
||||
var (
|
||||
setNickMutex sync.Mutex
|
||||
setEmailMutex sync.Mutex
|
||||
)
|
||||
|
||||
// UserID represents a user ID.
|
||||
type UserID = ID
|
||||
@ -62,6 +55,7 @@ type User struct {
|
||||
Browser UserBrowser `json:"browser" private:"true"`
|
||||
OS UserOS `json:"os" private:"true"`
|
||||
Location *Location `json:"location" private:"true"`
|
||||
FollowIDs []UserID `json:"follows"`
|
||||
|
||||
hasPosts
|
||||
|
||||
@ -124,9 +118,6 @@ func RegisterUser(user *User) {
|
||||
Items: []*PushSubscription{},
|
||||
})
|
||||
|
||||
// Add empty follow list
|
||||
NewUserFollows(user.ID).Save()
|
||||
|
||||
// Add empty notifications list
|
||||
NewUserNotifications(user.ID).Save()
|
||||
|
||||
|
21
arn/User.init.go
Normal file
21
arn/User.init.go
Normal file
@ -0,0 +1,21 @@
|
||||
package arn
|
||||
|
||||
import "github.com/aerogo/api"
|
||||
|
||||
// Register data lists.
|
||||
func init() {
|
||||
DataLists["genders"] = []*Option{
|
||||
// &Option{"", "Prefer not to say"},
|
||||
{"male", "Male"},
|
||||
{"female", "Female"},
|
||||
}
|
||||
|
||||
// Actions
|
||||
API.RegisterActions("User", []*api.Action{
|
||||
// Add follow
|
||||
FollowAction(),
|
||||
|
||||
// Remove follow
|
||||
UnfollowAction(),
|
||||
})
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
package arn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/aerogo/nano"
|
||||
)
|
||||
|
||||
@ -12,130 +10,6 @@ type UserFollows struct {
|
||||
Items []string `json:"items"`
|
||||
}
|
||||
|
||||
// NewUserFollows creates a new UserFollows list.
|
||||
func NewUserFollows(userID UserID) *UserFollows {
|
||||
return &UserFollows{
|
||||
UserID: userID,
|
||||
Items: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
// Add adds an user to the list if it hasn't been added yet.
|
||||
func (list *UserFollows) Add(userID UserID) error {
|
||||
if userID == list.UserID {
|
||||
return errors.New("You can't follow yourself")
|
||||
}
|
||||
|
||||
if list.Contains(userID) {
|
||||
return errors.New("User " + userID + " has already been added")
|
||||
}
|
||||
|
||||
list.Items = append(list.Items, userID)
|
||||
|
||||
// Send notification
|
||||
user, err := GetUser(userID)
|
||||
|
||||
if err == nil {
|
||||
if !user.Settings().Notification.NewFollowers {
|
||||
return nil
|
||||
}
|
||||
|
||||
follower, err := GetUser(list.UserID)
|
||||
|
||||
if err == nil {
|
||||
user.SendNotification(&PushNotification{
|
||||
Title: "You have a new follower!",
|
||||
Message: follower.Nick + " started following you.",
|
||||
Icon: "https:" + follower.AvatarLink("large"),
|
||||
Link: "https://notify.moe" + follower.Link(),
|
||||
Type: NotificationTypeFollow,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove removes the user ID from the list.
|
||||
func (list *UserFollows) Remove(userID UserID) bool {
|
||||
for index, item := range list.Items {
|
||||
if item == userID {
|
||||
list.Items = append(list.Items[:index], list.Items[index+1:]...)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Contains checks if the list contains the user ID already.
|
||||
func (list *UserFollows) Contains(userID UserID) bool {
|
||||
for _, item := range list.Items {
|
||||
if item == userID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Users returns a slice of all the users you are following.
|
||||
func (list *UserFollows) Users() []*User {
|
||||
followsObj := DB.GetMany("User", list.Items)
|
||||
follows := make([]*User, len(followsObj))
|
||||
|
||||
for i, obj := range followsObj {
|
||||
follows[i] = obj.(*User)
|
||||
}
|
||||
|
||||
return follows
|
||||
}
|
||||
|
||||
// UsersWhoFollowBack returns a slice of all the users you are following that also follow you.
|
||||
func (list *UserFollows) UsersWhoFollowBack() []*User {
|
||||
followsObj := DB.GetMany("User", list.Items)
|
||||
friends := make([]*User, 0, len(followsObj))
|
||||
|
||||
for _, obj := range followsObj {
|
||||
friend := obj.(*User)
|
||||
|
||||
if Contains(friend.Follows().Items, list.UserID) {
|
||||
friends = append(friends, friend)
|
||||
}
|
||||
}
|
||||
|
||||
return friends
|
||||
}
|
||||
|
||||
// GetID returns the ID.
|
||||
func (list *UserFollows) GetID() string {
|
||||
return list.UserID
|
||||
}
|
||||
|
||||
// UserFollowerCountMap returns a map of user ID keys and their corresping number of followers as the value.
|
||||
func UserFollowerCountMap() map[string]int {
|
||||
followCount := map[string]int{}
|
||||
|
||||
for list := range StreamUserFollows() {
|
||||
for _, followUserID := range list.Items {
|
||||
followCount[followUserID]++
|
||||
}
|
||||
}
|
||||
|
||||
return followCount
|
||||
}
|
||||
|
||||
// GetUserFollows ...
|
||||
func GetUserFollows(id UserID) (*UserFollows, error) {
|
||||
obj, err := DB.Get("UserFollows", id)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*UserFollows), nil
|
||||
}
|
||||
|
||||
// StreamUserFollows returns a stream of all user follows.
|
||||
func StreamUserFollows() <-chan *UserFollows {
|
||||
channel := make(chan *UserFollows, nano.ChannelBufferSize)
|
||||
@ -150,14 +24,3 @@ func StreamUserFollows() <-chan *UserFollows {
|
||||
|
||||
return channel
|
||||
}
|
||||
|
||||
// AllUserFollows returns a slice of all user follows.
|
||||
func AllUserFollows() ([]*UserFollows, error) {
|
||||
all := make([]*UserFollows, 0, DB.Collection("UserFollows").Count())
|
||||
|
||||
for obj := range StreamUserFollows() {
|
||||
all = append(all, obj)
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
||||
|
180
arn/UserFollows2.go
Normal file
180
arn/UserFollows2.go
Normal file
@ -0,0 +1,180 @@
|
||||
package arn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/aerogo/aero"
|
||||
"github.com/aerogo/api"
|
||||
)
|
||||
|
||||
// Add adds an user to the user if it hasn't been added yet.
|
||||
func (user *User) Follow(followUserID UserID) error {
|
||||
if followUserID == user.ID {
|
||||
return errors.New("You can't follow yourself")
|
||||
}
|
||||
|
||||
if user.IsFollowing(followUserID) {
|
||||
return errors.New("User " + followUserID + " has already been added")
|
||||
}
|
||||
|
||||
user.FollowIDs = append(user.FollowIDs, followUserID)
|
||||
|
||||
// Send notification
|
||||
user, err := GetUser(followUserID)
|
||||
|
||||
if err == nil {
|
||||
if !user.Settings().Notification.NewFollowers {
|
||||
return nil
|
||||
}
|
||||
|
||||
follower, err := GetUser(user.ID)
|
||||
|
||||
if err == nil {
|
||||
user.SendNotification(&PushNotification{
|
||||
Title: "You have a new follower!",
|
||||
Message: follower.Nick + " started following you.",
|
||||
Icon: "https:" + follower.AvatarLink("large"),
|
||||
Link: "https://notify.moe" + follower.Link(),
|
||||
Type: NotificationTypeFollow,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unfollow removes the user ID from the follow list.
|
||||
func (user *User) Unfollow(userID UserID) bool {
|
||||
for index, item := range user.FollowIDs {
|
||||
if item == userID {
|
||||
user.FollowIDs = append(user.FollowIDs[:index], user.FollowIDs[index+1:]...)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsFollowing checks if the object follows the user ID.
|
||||
func (user *User) IsFollowing(userID UserID) bool {
|
||||
for _, item := range user.FollowIDs {
|
||||
if item == userID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Follows returns a slice of all the users you are following.
|
||||
func (user *User) Follows() []*User {
|
||||
followsObj := DB.GetMany("User", user.FollowIDs)
|
||||
follows := make([]*User, len(followsObj))
|
||||
|
||||
for i, user := range followsObj {
|
||||
follows[i] = user.(*User)
|
||||
}
|
||||
|
||||
return follows
|
||||
}
|
||||
|
||||
// Friends returns a slice of all the users you are following that also follow you.
|
||||
func (user *User) Friends() []*User {
|
||||
followsObj := DB.GetMany("User", user.FollowIDs)
|
||||
friends := make([]*User, 0, len(followsObj))
|
||||
|
||||
for _, friendObj := range followsObj {
|
||||
friend := friendObj.(*User)
|
||||
|
||||
if friend.IsFollowing(user.ID) {
|
||||
friends = append(friends, friend)
|
||||
}
|
||||
}
|
||||
|
||||
return friends
|
||||
}
|
||||
|
||||
// Followers returns the users who follow the user.
|
||||
func (user *User) Followers() []*User {
|
||||
var followerIDs []string
|
||||
|
||||
for follower := range StreamUsers() {
|
||||
if follower.IsFollowing(user.ID) {
|
||||
followerIDs = append(followerIDs, follower.ID)
|
||||
}
|
||||
}
|
||||
|
||||
usersObj := DB.GetMany("User", followerIDs)
|
||||
users := make([]*User, len(usersObj))
|
||||
|
||||
for i, obj := range usersObj {
|
||||
users[i] = obj.(*User)
|
||||
}
|
||||
|
||||
return users
|
||||
}
|
||||
|
||||
// FollowersCount returns how many followers the user has.
|
||||
func (user *User) FollowersCount() int {
|
||||
count := 0
|
||||
|
||||
for follower := range StreamUsers() {
|
||||
if follower.IsFollowing(user.ID) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
// UserFollowerCountMap returns a map of user ID keys and their corresping number of followers as the value.
|
||||
func UserFollowerCountMap() map[string]int {
|
||||
followCount := map[string]int{}
|
||||
|
||||
for user := range StreamUsers() {
|
||||
for _, followUserID := range user.FollowIDs {
|
||||
followCount[followUserID]++
|
||||
}
|
||||
}
|
||||
|
||||
return followCount
|
||||
}
|
||||
|
||||
// FollowAction returns an API action that adds a user ID to the follow list.
|
||||
func FollowAction() *api.Action {
|
||||
return &api.Action{
|
||||
Name: "follow",
|
||||
Route: "/follow/:follow-id",
|
||||
Run: func(obj interface{}, ctx aero.Context) error {
|
||||
user := obj.(*User)
|
||||
followID := ctx.Get("follow-id")
|
||||
err := user.Follow(followID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user.Save()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// UnfollowAction returns an API action that removes a user ID from the follow list.
|
||||
func UnfollowAction() *api.Action {
|
||||
return &api.Action{
|
||||
Name: "unfollow",
|
||||
Route: "/unfollow/:unfollow-id",
|
||||
Run: func(obj interface{}, ctx aero.Context) error {
|
||||
user := obj.(*User)
|
||||
unfollowID := ctx.Get("unfollow-id")
|
||||
|
||||
if !user.Unfollow(unfollowID) {
|
||||
return errors.New("This item does not exist in the list")
|
||||
}
|
||||
|
||||
user.Save()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package arn
|
||||
|
||||
import (
|
||||
"github.com/aerogo/aero"
|
||||
"github.com/aerogo/api"
|
||||
)
|
||||
|
||||
// Force interface implementations
|
||||
var (
|
||||
_ Identifiable = (*UserFollows)(nil)
|
||||
_ IDCollection = (*UserFollows)(nil)
|
||||
_ api.Editable = (*UserFollows)(nil)
|
||||
)
|
||||
|
||||
// Actions
|
||||
func init() {
|
||||
API.RegisterActions("UserFollows", []*api.Action{
|
||||
// Add follow
|
||||
AddAction(),
|
||||
|
||||
// Remove follow
|
||||
RemoveAction(),
|
||||
})
|
||||
}
|
||||
|
||||
// Authorize returns an error if the given API request is not authorized.
|
||||
func (list *UserFollows) Authorize(ctx aero.Context, action string) error {
|
||||
return AuthorizeIfLoggedInAndOwnData(ctx, "id")
|
||||
}
|
||||
|
||||
// Save saves the follow list in the database.
|
||||
func (list *UserFollows) Save() {
|
||||
DB.Set("UserFollows", list.UserID, list)
|
||||
}
|
@ -42,51 +42,12 @@ func (user *User) Inventory() *Inventory {
|
||||
return inventory
|
||||
}
|
||||
|
||||
// Follows returns the list of user follows.
|
||||
func (user *User) Follows() *UserFollows {
|
||||
follows, _ := GetUserFollows(user.ID)
|
||||
return follows
|
||||
}
|
||||
|
||||
// Notifications returns the list of user notifications.
|
||||
func (user *User) Notifications() *UserNotifications {
|
||||
notifications, _ := GetUserNotifications(user.ID)
|
||||
return notifications
|
||||
}
|
||||
|
||||
// Followers ...
|
||||
func (user *User) Followers() []*User {
|
||||
var followerIDs []string
|
||||
|
||||
for list := range StreamUserFollows() {
|
||||
if list.Contains(user.ID) {
|
||||
followerIDs = append(followerIDs, list.UserID)
|
||||
}
|
||||
}
|
||||
|
||||
usersObj := DB.GetMany("User", followerIDs)
|
||||
users := make([]*User, len(usersObj))
|
||||
|
||||
for i, obj := range usersObj {
|
||||
users[i] = obj.(*User)
|
||||
}
|
||||
|
||||
return users
|
||||
}
|
||||
|
||||
// FollowersCount ...
|
||||
func (user *User) FollowersCount() int {
|
||||
count := 0
|
||||
|
||||
for list := range StreamUserFollows() {
|
||||
if list.Contains(user.ID) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
// DraftIndex ...
|
||||
func (user *User) DraftIndex() *DraftIndex {
|
||||
draftIndex, _ := GetDraftIndex(user.ID)
|
||||
|
@ -21,14 +21,8 @@ func Followed(ctx aero.Context) error {
|
||||
|
||||
// fetchActivities filters the activities by the given filters.
|
||||
func fetchActivities(user *arn.User, followedOnly bool) []arn.Activity {
|
||||
var followedUserIDs []string
|
||||
|
||||
if followedOnly && user != nil {
|
||||
followedUserIDs = user.Follows().Items
|
||||
}
|
||||
|
||||
activities := arn.FilterActivityCreates(func(activity arn.Activity) bool {
|
||||
if followedOnly && !arn.Contains(followedUserIDs, activity.GetCreatedBy()) {
|
||||
if followedOnly && user != nil && !user.IsFollowing(activity.GetCreatedBy()) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ func Get(ctx aero.Context) error {
|
||||
episodeToFriends := map[int][]*arn.User{}
|
||||
|
||||
if user != nil {
|
||||
friends = user.Follows().Users()
|
||||
friends = user.Follows()
|
||||
deleted := 0
|
||||
|
||||
if animeListItem != nil {
|
||||
|
@ -23,7 +23,7 @@ func Episodes(ctx aero.Context) error {
|
||||
episodeToFriends[ownListItem.Episodes] = append(episodeToFriends[ownListItem.Episodes], user)
|
||||
}
|
||||
|
||||
for _, friend := range user.Follows().Users() {
|
||||
for _, friend := range user.Follows() {
|
||||
friendAnimeList := friend.AnimeList()
|
||||
friendAnimeListItem := friendAnimeList.Find(anime.ID)
|
||||
|
||||
|
@ -126,7 +126,7 @@ func Get(ctx aero.Context) error {
|
||||
var friends []*arn.User
|
||||
|
||||
if user != nil {
|
||||
friendIDs := utils.Intersection(character.Likes, user.Follows().Items)
|
||||
friendIDs := utils.Intersection(character.Likes, user.FollowIDs)
|
||||
friendObjects := arn.DB.GetMany("User", friendIDs)
|
||||
|
||||
for _, obj := range friendObjects {
|
||||
|
@ -120,7 +120,7 @@ func Profile(ctx aero.Context, viewUser *arn.User) error {
|
||||
}
|
||||
|
||||
// Friends
|
||||
friends := viewUser.Follows().UsersWhoFollowBack()
|
||||
friends := viewUser.Friends()
|
||||
|
||||
arn.SortUsersFollowers(friends)
|
||||
|
||||
|
@ -141,12 +141,12 @@ component ProfileHead(viewUser *arn.User, animeList *arn.AnimeList, user *arn.Us
|
||||
|
||||
.profile-actions
|
||||
if user != nil && user.ID != viewUser.ID
|
||||
if !user.Follows().Contains(viewUser.ID)
|
||||
button.profile-action.action.mountable.never-unmount(data-action="followUser", data-trigger="click", data-api="/api/userfollows/" + user.ID + "/add/" + viewUser.ID)
|
||||
if !user.IsFollowing(viewUser.ID)
|
||||
button.profile-action.action.mountable.never-unmount(data-action="followUser", data-trigger="click", data-api="/api/user/" + user.ID + "/follow/" + viewUser.ID)
|
||||
Icon("user-plus")
|
||||
span Follow
|
||||
else
|
||||
button.profile-action.action.mountable.never-unmount(data-action="unfollowUser", data-trigger="click", data-api="/api/userfollows/" + user.ID + "/remove/" + viewUser.ID)
|
||||
button.profile-action.action.mountable.never-unmount(data-action="unfollowUser", data-trigger="click", data-api="/api/user/" + user.ID + "/unfollow/" + viewUser.ID)
|
||||
Icon("user-times")
|
||||
span Unfollow
|
||||
|
||||
|
22
patches/user-copy-follows/user-copy-follow.go
Normal file
22
patches/user-copy-follows/user-copy-follow.go
Normal file
@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/akyoto/color"
|
||||
"github.com/animenotifier/notify.moe/arn"
|
||||
)
|
||||
|
||||
func main() {
|
||||
defer arn.Node.Close()
|
||||
|
||||
for follows := range arn.StreamUserFollows() {
|
||||
user, err := arn.GetUser(follows.UserID)
|
||||
|
||||
if err != nil {
|
||||
color.Red(err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
user.FollowIDs = follows.Items
|
||||
user.Save()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user