Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More context menus and read behavior #173

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Swiftcord/Utils/Extensions/MessagesView+.swift
Original file line number Diff line number Diff line change
@@ -74,7 +74,7 @@ internal extension MessagesView {

Task {
do {
_ = try await restAPI.createChannelMsg(
let newMessage = try await restAPI.createChannelMsg(
message: NewMessage(
content: message,
allowed_mentions: allowedMentions,
@@ -90,6 +90,8 @@ internal extension MessagesView {
attachments: attachments,
id: ctx.channel!.id
)

_ = try await restAPI.ackMessageRead(id: newMessage.channel_id, msgID: newMessage.id)
} catch {
viewModel.showingInfoBar = true
viewModel.infoBarData = InfoBarData(
1 change: 1 addition & 0 deletions Swiftcord/Views/ContentView.swift
Original file line number Diff line number Diff line change
@@ -109,6 +109,7 @@ struct ContentView: View {
case .guild(let guild):
ServerButton(
selected: state.selectedGuildID == guild.id || loadingGuildID == guild.id,
guild: guild,
name: guild.properties.name,
serverIconURL: guild.properties.iconURL(),
isLoading: loadingGuildID == guild.id
21 changes: 21 additions & 0 deletions Swiftcord/Views/Message/MessageRenderViews/MessageView.swift
Original file line number Diff line number Diff line change
@@ -41,6 +41,7 @@ struct MessageView: View, Equatable {
}

let message: Message
let prevMessage: Message?
let shrunk: Bool
let quotedMsg: Message?
let onQuoteClick: (Snowflake) -> Void
@@ -159,6 +160,12 @@ struct MessageView: View, Equatable {
Text("Edit")
}
}

Button(action: { Task { await readMessage() } }) {
Image(systemName: "message.badge")
Text("Mark as unread")
}

Button(role: .destructive, action: deleteMessage) {
Image(systemName: "xmark.bin.fill")
Text("Delete Message").foregroundColor(.red)
@@ -209,6 +216,20 @@ private extension MessageView {
func editMessage() {
print(#function)
}

func readMessage() async {
do {
let id = Int(floor((message.id as NSString).doubleValue / pow(2, 22)) - 1)
var defaultId: Snowflake
if id < 0 {
defaultId = "0"
} else {
defaultId = String(id << 22)
}

let _ = try await restAPI.ackMessageRead(id: message.channel_id, msgID: prevMessage?.id ?? defaultId, manual: true, mention_count: 0)
} catch {}
}

func deleteMessage() {
Task {
1 change: 1 addition & 0 deletions Swiftcord/Views/Message/MessagesView.swift
Original file line number Diff line number Diff line change
@@ -159,6 +159,7 @@ struct MessagesView: View {
func cell(for msg: Message, shrunk: Bool) -> some View {
MessageView(
message: msg,
prevMessage: viewModel.messages.after(msg),
shrunk: shrunk,
quotedMsg: msg.message_reference != nil
? viewModel.messages.first {
65 changes: 65 additions & 0 deletions Swiftcord/Views/Server/ChannelList.swift
Original file line number Diff line number Diff line change
@@ -29,6 +29,26 @@ struct ChannelList: View, Equatable {
Circle().fill(.primary).frame(width: 8, height: 8).offset(x: 2)
}
})
.contextMenu {
let isRead = gateway.readState[channel.id]?.id == channel.last_message_id
Button(action: { Task { await readChannel(channel) } }) {
Image(systemName: isRead ? "message" : "message.badge")
Text("Mark as read")
}.disabled(isRead)

Divider()

Group {
Button(action: { copyLink(channel) }) {
Image(systemName: "link")
Text("Copy Link")
}
Button(action: { copyId(channel) }) {
Image(systemName: "number.circle.fill")
Text("Copy ID")
}
}
}
}

var body: some View {
@@ -77,6 +97,19 @@ struct ChannelList: View, Equatable {
Section(header: Text(channel.name ?? "").textCase(.uppercase).padding(.leading, 8)) {
ForEach(channels, id: \.id) { channel in item(for: channel) }
}
.contextMenu {
Button(action: { Task { await readChannels(channels) } }) {
Image(systemName: "message.badge")
Text("Mark as read")
}

Divider()

Button(action: { copyId(channel) }) {
Image(systemName: "number.circle.fill")
Text("Copy ID")
}
}
}
}
}
@@ -96,3 +129,35 @@ struct ChannelList: View, Equatable {
lhs.channels == rhs.channels && lhs.selCh == rhs.selCh
}
}

private extension ChannelList {
func readChannels(_ channels: [Channel]) async {
for channel in channels {
await readChannel(channel)
}
}

func readChannel(_ channel: Channel) async {
do {
let _ = try await restAPI.ackMessageRead(id: channel.id, msgID: channel.last_message_id ?? "", manual: true, mention_count: 0)
} catch {}
}

func copyLink(_ channel: Channel) {
let pasteboard = NSPasteboard.general
pasteboard.clearContents()
pasteboard.setString(
"https://canary.discord.com/channels/\(channel.guild_id ?? "@me")/\(channel.id)",
forType: .string
)
}

func copyId(_ channel: Channel) {
let pasteboard = NSPasteboard.general
pasteboard.clearContents()
pasteboard.setString(
channel.id,
forType: .string
)
}
}
106 changes: 83 additions & 23 deletions Swiftcord/Views/Server/ServerButton.swift
Original file line number Diff line number Diff line change
@@ -6,6 +6,8 @@
//

import SwiftUI
import DiscordKit
import DiscordKitCore
import CachedAsyncImage

/*
@@ -26,6 +28,7 @@ import CachedAsyncImage

struct ServerButton: View {
let selected: Bool
var guild: PreloadedGuild?
let name: String
var systemIconName: String?
var assetIconName: String?
@@ -51,6 +54,7 @@ struct ServerButton: View {
.buttonStyle(
ServerButtonStyle(
selected: selected,
guild: guild,
name: name,
bgColor: bgColor,
systemName: systemIconName,
@@ -60,23 +64,33 @@ struct ServerButton: View {
hovered: $hovered
)
)
.padding(.trailing, 8)

.popover(isPresented: $hovered) {
Text(name)
.font(.title3)
.padding(8)
.frame(maxWidth: 300)
.interactiveDismissDisabled()
}
.padding(.trailing, 8)

Spacer()
}
.frame(width: 72, height: 48)
}
}

struct ServerButtonStyle: ButtonStyle {
let selected: Bool
let name: String
let bgColor: Color?
let systemName: String?
let assetName: String?
let serverIconURL: String?
let loading: Bool
@Binding var hovered: Bool
let selected: Bool
var guild: PreloadedGuild?
let name: String
let bgColor: Color?
let systemName: String?
let assetName: String?
let serverIconURL: String?
let loading: Bool
@Binding var hovered: Bool

@EnvironmentObject var gateway: DiscordGateway

func makeBody(configuration: Configuration) -> some View {
ZStack {
@@ -129,20 +143,66 @@ struct ServerButtonStyle: ButtonStyle {
}
.offset(y: configuration.isPressed ? 1 : 0)
.animation(.none, value: configuration.isPressed)
.animation(.interpolatingSpring(stiffness: 500, damping: 30), value: hovered)
.onHover { hover in hovered = hover }
.animation(.interpolatingSpring(stiffness: 500, damping: 30), value: hovered)
.onHover { hover in hovered = hover }
.contextMenu {
if guild != nil {
Text(name)

Divider()

Button(action: { Task { await readAll() } }) {
Image(systemName: "message.badge")
Text("Mark as read")
}

Divider()

Group {
Button(action: copyLink) {
Image(systemName: "link")
Text("Copy Link")
}
Button(action: copyId) {
Image(systemName: "number.circle.fill")
Text("Copy ID")
}
}
}
}
}
}

struct ServerButton_Previews: PreviewProvider {
static var previews: some View {
ServerButton(
selected: false,
name: "Hello world, discord!",
systemIconName: nil,
assetIconName: nil,
serverIconURL: nil,
bgColor: nil
) {}
}
private extension ServerButtonStyle {
func readAll() async {
if let guild = guild {
for channel in guild.channels {
do {
let _ = try await restAPI.ackMessageRead(id: channel.id, msgID: channel.last_message_id ?? "", manual: true, mention_count: 0)
} catch {}
}
}
}

func copyLink() {
if let guild = guild {
let pasteboard = NSPasteboard.general
pasteboard.clearContents()
pasteboard.setString(
"https://canary.discord.com/channels/\(guild.id)",
forType: .string
)
}
}

func copyId() {
if let guild = guild {
let pasteboard = NSPasteboard.general
pasteboard.clearContents()
pasteboard.setString(
guild.id,
forType: .string
)
}
}
}
4 changes: 3 additions & 1 deletion Swiftcord/Views/Server/ServerFolder.swift
Original file line number Diff line number Diff line change
@@ -80,14 +80,16 @@ struct ServerFolder: View {
Text(folder.name)
.font(.title3)
.padding(10)
// Prevent popover from blocking clicks to other views
.frame(maxWidth: 300)
// Prevent popover from blocking clicks to other views
.interactiveDismissDisabled()
}

if open {
ForEach(folder.guilds, id: \.id) { [self] guild in
ServerButton(
selected: selectedGuildID == guild.id || loadingGuildID == guild.id,
guild: guild,
name: guild.properties.name,
serverIconURL: guild.properties.icon != nil ? "\(DiscordKitConfig.default.cdnURL)icons/\(guild.id)/\(guild.properties.icon!).webp?size=240" : nil,
isLoading: loadingGuildID == guild.id