Added blocking/unblocking of users using b/u (user settings are now s

instead of "u")
This commit is contained in:
2025-02-12 13:08:49 +02:00
parent 976d3683f9
commit 66983fb028
10 changed files with 345 additions and 144 deletions

View File

@@ -29,10 +29,10 @@ var (
focusStyle = func() lipgloss.Style {
return lipgloss.NewStyle().Background(colors.Background).Foreground(colors.Focus)
}
readOnlyStyle = func() lipgloss.Style {
grayStyle = func() lipgloss.Style {
return lipgloss.NewStyle().Background(colors.Background).Foreground(colors.Gray)
}
mutedStyle = func() lipgloss.Style { return lipgloss.NewStyle().Background(colors.Background).Foreground(colors.Red) }
redStyle = func() lipgloss.Style { return lipgloss.NewStyle().Background(colors.Background).Foreground(colors.Red) }
editStyle = func() lipgloss.Style {
return lipgloss.NewStyle().Background(colors.Background).Foreground(colors.Gold)
}
@@ -45,8 +45,8 @@ var (
Padding(0, 1)
}
ViFocusedBorder = func() lipgloss.Style { return ViBlurredBorder().BorderForeground(colors.Focus) }
ViReadOnlyBorder = func() lipgloss.Style { return ViBlurredBorder().BorderForeground(colors.Gray) }
ViMutedBorder = func() lipgloss.Style { return ViBlurredBorder().BorderForeground(colors.Red) }
ViGrayBorder = func() lipgloss.Style { return ViBlurredBorder().BorderForeground(colors.Gray) }
ViRedBorder = func() lipgloss.Style { return ViBlurredBorder().BorderForeground(colors.Red) }
ViEditBorder = func() lipgloss.Style { return ViBlurredBorder().BorderForeground(colors.Gold) }
PaddingCount = 1
@@ -74,6 +74,8 @@ var (
ReadOnlyPlaceholder = "You do not have permission to send messages in this frequency"
MutedPlaceholder = "You have been muted by a network adminstrator"
SelectSignalOrFrequency = "Cannot send messages, select a signal or frequency first"
BlockedPlaceholder = "You have blocked this user"
BlockingPlaceholder = "You have been blocked by this user"
EditedIndicator = func(bg lipgloss.Color) string {
return lipgloss.NewStyle().Background(bg).
@@ -230,14 +232,14 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
if member.IsMuted {
m.vi.Placeholder = MutedPlaceholder
m.borderStyle = ViMutedBorder()
m.style = mutedStyle()
m.borderStyle = ViRedBorder()
m.style = redStyle()
m.vi.SetInactive(true)
m.locked = false
} else if frequency.Perms != packet.PermReadWrite && !member.IsAdmin {
m.vi.Placeholder = ReadOnlyPlaceholder
m.borderStyle = ViReadOnlyBorder()
m.style = readOnlyStyle()
m.borderStyle = ViGrayBorder()
m.style = grayStyle()
m.vi.SetInactive(true)
m.locked = false
} else if m.locked {
@@ -277,9 +279,23 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
}
m.hasReadAccess = true
m.hasWriteAccess = true // TODO: implement blocking users
m.hasWriteAccess = true
if m.locked {
if _, ok := state.State.BlockedUsers[receiverId]; ok {
m.hasWriteAccess = false
m.vi.Placeholder = BlockedPlaceholder
m.borderStyle = ViRedBorder()
m.style = redStyle()
m.vi.SetInactive(true)
m.locked = false
} else if _, ok := state.State.BlockingUsers[receiverId]; ok {
m.hasWriteAccess = false
m.vi.Placeholder = BlockingPlaceholder
m.borderStyle = ViRedBorder()
m.style = redStyle()
m.vi.SetInactive(true)
m.locked = false
} else if m.locked {
m.vi.Placeholder = SendMessagePlaceholder
m.borderStyle = ViFocusedBorder()
m.style = focusStyle()
@@ -301,8 +317,8 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
m.hasWriteAccess = false // TODO: implement blocking users
m.vi.Placeholder = SelectSignalOrFrequency
m.borderStyle = ViReadOnlyBorder()
m.style = readOnlyStyle()
m.borderStyle = ViGrayBorder()
m.style = grayStyle()
m.vi.SetInactive(true)
}
@@ -441,11 +457,55 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
_, isTrusting := state.State.TrustedUsers[senderId]
_, isBlocked := state.State.BlockedUsers[senderId]
if !isTrusting && isBlocked {
return m, nil
}
return m, gateway.Send(&packet.TrustUser{
User: senderId,
Trust: !isTrusting,
})
case "b":
if m.selectedMessage == nil {
return m, nil
}
userId := m.selectedMessage.SenderID
if userId == *state.UserID {
return m, nil
}
if _, ok := state.State.BlockedUsers[userId]; ok {
return m, nil
}
return m, gateway.Send(&packet.BlockUser{
User: userId,
Block: true,
})
case "u":
if m.selectedMessage == nil {
return m, nil
}
userId := m.selectedMessage.SenderID
if userId == *state.UserID {
return m, nil
}
if _, ok := state.State.BlockedUsers[userId]; !ok {
return m, nil
}
return m, gateway.Send(&packet.BlockUser{
User: userId,
Block: false,
})
// Admin
case "K":
if m.selectedMessage == nil || m.receiverIndex != -1 {
@@ -514,106 +574,70 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
User: member.UserID,
})
case "U":
if m.selectedMessage == nil {
if m.selectedMessage == nil || m.receiverIndex != -1 {
return m, nil
}
networkId := state.NetworkId(m.networkIndex)
network := state.State.Networks[*networkId]
senderId := m.selectedMessage.SenderID
member := state.State.Members[*networkId][senderId]
if !member.IsMuted {
return m, nil
}
if m.receiverIndex != -1 {
userId := state.Data.Signals[m.receiverIndex]
if userId == *state.UserID {
return m, nil
}
if _, ok := state.State.BlockedUsers[userId]; !ok {
return m, nil
}
return m, gateway.Send(&packet.BlockUser{
User: userId,
Block: false,
})
} else {
networkId := state.NetworkId(m.networkIndex)
network := state.State.Networks[*networkId]
senderId := m.selectedMessage.SenderID
member := state.State.Members[*networkId][senderId]
if !member.IsMuted {
return m, nil
}
if !state.State.Members[*networkId][*state.UserID].IsAdmin {
return m, nil
}
if member.UserID == *state.UserID {
return m, nil
}
if member.IsAdmin && network.OwnerID != *state.UserID {
return m, nil
}
no := false
return m, gateway.Send(&packet.SetMember{
Member: nil,
Admin: nil,
Muted: &no,
Banned: nil,
BanReason: nil,
Network: *state.NetworkId(m.networkIndex),
User: member.UserID,
})
if !state.State.Members[*networkId][*state.UserID].IsAdmin {
return m, nil
}
if member.UserID == *state.UserID {
return m, nil
}
if member.IsAdmin && network.OwnerID != *state.UserID {
return m, nil
}
no := false
return m, gateway.Send(&packet.SetMember{
Member: nil,
Admin: nil,
Muted: &no,
Banned: nil,
BanReason: nil,
Network: *state.NetworkId(m.networkIndex),
User: member.UserID,
})
case "B":
if m.selectedMessage == nil {
if m.selectedMessage == nil || m.receiverIndex != -1 {
return m, nil
}
if m.receiverIndex != -1 {
userId := state.Data.Signals[m.receiverIndex]
networkId := state.NetworkId(m.networkIndex)
network := state.State.Networks[*networkId]
senderId := m.selectedMessage.SenderID
member := state.State.Members[*networkId][senderId]
if userId == *state.UserID {
return m, nil
}
if _, ok := state.State.BlockedUsers[userId]; ok {
return m, nil
}
return m, gateway.Send(&packet.BlockUser{
User: userId,
Block: true,
})
} else {
networkId := state.NetworkId(m.networkIndex)
network := state.State.Networks[*networkId]
senderId := m.selectedMessage.SenderID
member := state.State.Members[*networkId][senderId]
if !state.State.Members[*networkId][*state.UserID].IsAdmin {
return m, nil
}
if member.UserID == *state.UserID {
return m, nil
}
if member.IsAdmin && network.OwnerID != *state.UserID {
return m, nil
}
cmd := func() tea.Msg {
return ui.BanReasonPopupMsg{
Network: *state.NetworkId(m.networkIndex),
User: member.UserID,
}
}
return m, cmd
if !state.State.Members[*networkId][*state.UserID].IsAdmin {
return m, nil
}
if member.UserID == *state.UserID {
return m, nil
}
if member.IsAdmin && network.OwnerID != *state.UserID {
return m, nil
}
cmd := func() tea.Msg {
return ui.BanReasonPopupMsg{
Network: *state.NetworkId(m.networkIndex),
User: member.UserID,
}
}
return m, cmd
// Owner
case "D":
if m.selectedMessage == nil || m.receiverIndex != -1 {
@@ -1194,6 +1218,13 @@ func (m *Model) renderMessageGroup(group []data.Message, remaining *int, height
}
}
if _, ok := state.State.BlockedUsers[group[i].SenderID]; ok {
messageStyle = pingedMessageStyle.
BorderForeground(colors.Gray).
Background(colors.DarkGray)
backgroundStyle = backgroundStyle.Background(colors.DarkGray).Foreground(colors.DarkGray)
}
rawContent := extra + backgroundStyle.Render(group[i].Content)
content := messageStyle.Render(rawContent)
heights[i] = lipgloss.Height(content)
@@ -1400,6 +1431,10 @@ func (m *Model) renderHeader(message data.Message, selected bool) []byte {
buf = append(buf, []byte(MutedSymbol())...)
}
if _, ok := state.State.BlockedUsers[user.ID]; ok {
buf = append(buf, ui.BlockedSymbol()...)
}
} else if m.receiverIndex != -1 {
user := state.State.Users[message.SenderID]
trustedPublicKey, isTrusted := state.State.TrustedUsers[user.ID]
@@ -1420,7 +1455,6 @@ func (m *Model) renderHeader(message data.Message, selected bool) []byte {
buf = append(buf, sender...)
if _, ok := state.State.BlockedUsers[user.ID]; ok {
buf = append(buf, ' ')
buf = append(buf, ui.BlockedSymbol()...)
}
}

View File

@@ -431,8 +431,8 @@ func (m *Model) updateConnected(message tea.Msg) tea.Cmd {
message = ui.EmptyMsg{}
}
// user
case "u":
// user [s]ettings
case "s":
isChatLocked := m.focus == FocusChat && m.chat.Locked()
if !m.HasPopup() && !isChatLocked {
popup := usersettings.New()

View File

@@ -212,6 +212,11 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
_, isTrusting := state.State.TrustedUsers[member.UserID]
_, isBlocked := state.State.BlockedUsers[member.UserID]
if !isTrusting && isBlocked {
return m, nil
}
return m, gateway.Send(&packet.TrustUser{
User: member.UserID,
Trust: !isTrusting,

View File

@@ -91,7 +91,6 @@ func (m Model) View() string {
Foreground(colors.Red).Background(colors.BackgroundDim).
Render(notif)
maxUserWidth -= notifWidth
}
if m.index == m.base+i {
@@ -107,6 +106,12 @@ func (m Model) View() string {
username = ui.UntrustedSymbol() + username
}
blockSymbol := ""
if _, ok := state.State.BlockedUsers[user.ID]; ok {
maxUserWidth -= 2 // blocked symbol width
blockSymbol = ui.BlockedSymbol()
}
if lipgloss.Width(username) <= maxUserWidth {
username = lipgloss.NewStyle().
MaxWidth(maxUserWidth).
@@ -120,11 +125,15 @@ func (m Model) View() string {
Render(username) + ellipsisStyle.Render(ellipsis)
}
if m.index == m.base+i {
blockSymbol = lipgloss.NewStyle().
Background(colors.BackgroundHighlight).Render(blockSymbol)
username = lipgloss.NewStyle().Width(maxUserWidth).
Background(colors.BackgroundHighlight).Render(username)
Background(colors.BackgroundHighlight).Render(username + blockSymbol)
} else {
blockSymbol = lipgloss.NewStyle().
Background(colors.BackgroundDim).Render(blockSymbol)
username = lipgloss.NewStyle().Width(maxUserWidth).
Background(colors.BackgroundDim).Render(username)
Background(colors.BackgroundDim).Render(username + blockSymbol)
}
signal := signalStyle.Render(username + notif)
@@ -193,11 +202,53 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
_, isTrusting := state.State.TrustedUsers[userId]
_, isBlocked := state.State.BlockedUsers[userId]
if !isTrusting && isBlocked {
return m, nil
}
return m, gateway.Send(&packet.TrustUser{
User: userId,
Trust: !isTrusting,
})
case "b":
if m.index == -1 {
return m, nil
}
userId := state.Data.Signals[m.index]
if userId == *state.UserID {
return m, nil
}
if _, ok := state.State.BlockedUsers[userId]; ok {
return m, nil
}
return m, gateway.Send(&packet.BlockUser{
User: userId,
Block: true,
})
case "u":
if m.index == -1 {
return m, nil
}
userId := state.Data.Signals[m.index]
if userId == *state.UserID {
return m, nil
}
if _, ok := state.State.BlockedUsers[userId]; !ok {
return m, nil
}
return m, gateway.Send(&packet.BlockUser{
User: userId,
Block: false,
})
}
}

View File

@@ -26,13 +26,14 @@ type state struct {
ChatState map[snowflake.ID]ChatState // key is frequency id or receiver id
LastFrequency map[snowflake.ID]snowflake.ID // key is network id
Messages map[snowflake.ID]*btree.BTreeG[data.Message] // key is frequency id or receiver id
Networks map[snowflake.ID]data.Network // key is network id
Frequencies map[snowflake.ID][]data.Frequency // key is network id
Members map[snowflake.ID]map[snowflake.ID]data.Member // key is network id then user id
Users map[snowflake.ID]data.User // key is user id
TrustedUsers map[snowflake.ID]ed25519.PublicKey // key is user id
BlockedUsers map[snowflake.ID]struct{} // key is user id
Messages map[snowflake.ID]*btree.BTreeG[data.Message] // key is frequency id or receiver id
Networks map[snowflake.ID]data.Network // key is network id
Frequencies map[snowflake.ID][]data.Frequency // key is network id
Members map[snowflake.ID]map[snowflake.ID]data.Member // key is network id then user id
Users map[snowflake.ID]data.User // key is user id
TrustedUsers map[snowflake.ID]ed25519.PublicKey // key is user id
BlockedUsers map[snowflake.ID]struct{} // key is user id
BlockingUsers map[snowflake.ID]struct{} // key is user id
LastReadMessages map[snowflake.ID]*snowflake.ID // key is frequency id or receiver id
Notifications map[snowflake.ID]int // key is frequency id or receiver id
@@ -48,6 +49,7 @@ var State state = state{
Users: map[snowflake.ID]data.User{},
TrustedUsers: map[snowflake.ID]ed25519.PublicKey{},
BlockedUsers: map[snowflake.ID]struct{}{},
BlockingUsers: map[snowflake.ID]struct{}{},
LastReadMessages: map[snowflake.ID]*snowflake.ID{},
Notifications: map[snowflake.ID]int{},
}
@@ -345,7 +347,16 @@ func UpdateBlockedUsers(info *packet.BlockInfo) {
delete(State.BlockedUsers, removed)
}
for _, trusted := range info.BlockedUsers {
State.BlockedUsers[trusted] = struct{}{}
for _, blocked := range info.BlockedUsers {
State.BlockedUsers[blocked] = struct{}{}
delete(State.TrustedUsers, blocked)
}
for _, removed := range info.RemovedBlockingUsers {
delete(State.BlockingUsers, removed)
}
for _, blocking := range info.BlockingUsers {
State.BlockingUsers[blocking] = struct{}{}
}
}

View File

@@ -37,7 +37,7 @@ var (
TrustedAdminStyle = func() lipgloss.Style { return AdminStyle().SetString("󱄻") }
TrustedOwnerStyle = func() lipgloss.Style { return OwnerStyle().SetString("󱢼") }
UntrustedSymbol = func() string { return lipgloss.NewStyle().Foreground(colors.Red).Render("󱈸") }
BlockedSymbol = func() string { return lipgloss.NewStyle().Foreground(colors.Red).Render("󱑙") }
BlockedSymbol = func() string { return lipgloss.NewStyle().Foreground(colors.Red).Render(" 󰅜") }
)
var NewAuth func() tea.Model

View File

@@ -56,6 +56,34 @@ func (q *Queries) GetBlockedUsers(ctx context.Context, blockingUserID snowflake.
return items, nil
}
const getBlockingUsers = `-- name: GetBlockingUsers :many
SELECT blocking_user_id FROM blocked_users
WHERE blocked_user_id = ?
`
func (q *Queries) GetBlockingUsers(ctx context.Context, blockedUserID snowflake.ID) ([]snowflake.ID, error) {
rows, err := q.db.QueryContext(ctx, getBlockingUsers, blockedUserID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []snowflake.ID
for rows.Next() {
var blocking_user_id snowflake.ID
if err := rows.Scan(&blocking_user_id); err != nil {
return nil, err
}
items = append(items, blocking_user_id)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTrustedPublicKey = `-- name: GetTrustedPublicKey :one
SELECT trusted_public_key FROM trusted_users
WHERE trusting_user_id = ? AND trusted_user_id = ?

View File

@@ -263,8 +263,10 @@ func (m *BlockUser) Type() PacketType {
}
type BlockInfo struct {
BlockedUsers []snowflake.ID
RemovedBlockedUsers []snowflake.ID
BlockedUsers []snowflake.ID
RemovedBlockedUsers []snowflake.ID
BlockingUsers []snowflake.ID
RemovedBlockingUsers []snowflake.ID
}
func (m *BlockInfo) Type() PacketType {

View File

@@ -1204,18 +1204,30 @@ func TrustUser(ctx context.Context, sess *session.Session, request *packet.Trust
RemovedTrustedUsers: nil,
}
}
if err != nil && err != sql.ErrNoRows {
if err != sql.ErrNoRows {
log.Println("database error 1:", err)
return &ErrInternalError
}
_, err = queries.IsUserBlocked(ctx, data.IsUserBlockedParams{
BlockingUserID: sess.ID(),
BlockedUserID: user.ID,
})
if err == nil {
return &packet.Error{Error: "cannot trust blocked user, unblock them first"}
}
if err != sql.ErrNoRows {
log.Println("database error 2:", err)
return &ErrInternalError
}
err = queries.TrustUser(ctx, data.TrustUserParams{
TrustingUserID: sess.ID(),
TrustedUserID: user.ID,
TrustedPublicKey: user.PublicKey,
})
if err != nil {
log.Println("database error 2:", err)
log.Println("database error 3:", err)
return &ErrInternalError
}
@@ -1230,7 +1242,7 @@ func TrustUser(ctx context.Context, sess *session.Session, request *packet.Trust
TrustedUserID: user.ID,
})
if err != nil {
log.Println("database error 3:", err)
log.Println("database error 4:", err)
return &ErrInternalError
}
@@ -1417,41 +1429,87 @@ func BlockUser(ctx context.Context, sess *session.Session, request *packet.Block
})
if err == nil {
return &packet.BlockInfo{
BlockedUsers: []snowflake.ID{user.ID},
RemovedBlockedUsers: nil,
BlockedUsers: []snowflake.ID{user.ID},
RemovedBlockedUsers: nil,
BlockingUsers: nil,
RemovedBlockingUsers: nil,
}
}
if err != nil && err != sql.ErrNoRows {
if err != sql.ErrNoRows {
log.Println("database error 1:", err)
return &ErrInternalError
}
err = queries.UntrustUser(ctx, data.UntrustUserParams{
TrustingUserID: sess.ID(),
TrustedUserID: user.ID,
})
if err != nil {
log.Println("database error 2:", err)
return &ErrInternalError
}
err = queries.BlockUser(ctx, data.BlockUserParams{
BlockingUserID: sess.ID(),
BlockedUserID: user.ID,
})
if err != nil {
log.Println("database error 2:", err)
return &ErrInternalError
}
return &packet.BlockInfo{
BlockedUsers: []snowflake.ID{user.ID},
RemovedBlockedUsers: nil,
}
} else {
err = queries.UntrustUser(ctx, data.UntrustUserParams{
TrustingUserID: sess.ID(),
TrustedUserID: user.ID,
})
if err != nil {
log.Println("database error 3:", err)
return &ErrInternalError
}
UserPropagate(ctx, sess, user.ID, &packet.BlockInfo{
BlockedUsers: nil,
RemovedBlockedUsers: nil,
BlockingUsers: []snowflake.ID{sess.ID()},
RemovedBlockingUsers: nil,
})
return &packet.BlockInfo{
BlockedUsers: nil,
RemovedBlockedUsers: []snowflake.ID{user.ID},
BlockedUsers: []snowflake.ID{user.ID},
RemovedBlockedUsers: nil,
BlockingUsers: nil,
RemovedBlockingUsers: nil,
}
} else {
_, err := queries.IsUserBlocked(ctx, data.IsUserBlockedParams{
BlockingUserID: sess.ID(),
BlockedUserID: user.ID,
})
if err == sql.ErrNoRows {
return &packet.BlockInfo{
BlockedUsers: nil,
RemovedBlockedUsers: []snowflake.ID{user.ID},
BlockingUsers: nil,
RemovedBlockingUsers: nil,
}
}
if err != nil {
log.Println("database error 4:", err)
return &ErrInternalError
}
err = queries.UnblockUser(ctx, data.UnblockUserParams{
BlockingUserID: sess.ID(),
BlockedUserID: user.ID,
})
if err != nil {
log.Println("database error 5:", err)
return &ErrInternalError
}
UserPropagate(ctx, sess, user.ID, &packet.BlockInfo{
BlockedUsers: nil,
RemovedBlockedUsers: nil,
BlockingUsers: nil,
RemovedBlockingUsers: []snowflake.ID{sess.ID()},
})
return &packet.BlockInfo{
BlockedUsers: nil,
RemovedBlockedUsers: []snowflake.ID{user.ID},
BlockingUsers: nil,
RemovedBlockingUsers: nil,
}
}
}
@@ -1465,8 +1523,16 @@ func GetBlockedUsers(ctx context.Context, sess *session.Session) packet.Payload
return &ErrInternalError
}
blockingUsers, err := queries.GetBlockingUsers(ctx, sess.ID())
if err != nil && err != sql.ErrNoRows {
log.Println("database error 1:", err)
return &ErrInternalError
}
return &packet.BlockInfo{
BlockedUsers: blockedUsers,
RemovedBlockedUsers: nil,
BlockedUsers: blockedUsers,
RemovedBlockedUsers: nil,
BlockingUsers: blockingUsers,
RemovedBlockingUsers: nil,
}
}

View File

@@ -19,6 +19,10 @@ WHERE trusting_user_id = ? AND trusted_user_id = ?;
SELECT blocked_user_id FROM blocked_users
WHERE blocking_user_id = ?;
-- name: GetBlockingUsers :many
SELECT blocking_user_id FROM blocked_users
WHERE blocked_user_id = ?;
-- name: IsUserBlocked :one
SELECT blocked_user_id FROM blocked_users
WHERE blocking_user_id = ? AND blocked_user_id = ?;