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
Show file tree
Hide file tree
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
Expand Up @@ -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,
Expand All @@ -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(
Expand Down
1 change: 1 addition & 0 deletions Swiftcord/Views/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions Swiftcord/Views/Message/MessageRenderViews/MessageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ struct MessageView: View, Equatable {
}

let message: Message
let prevMessage: Message?
let shrunk: Bool
let quotedMsg: Message?
let onQuoteClick: (Snowflake) -> Void
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions Swiftcord/Views/Message/MessagesView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
65 changes: 65 additions & 0 deletions Swiftcord/Views/Server/ChannelList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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")
}
}
}
}
}
Expand All @@ -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
Expand Up @@ -6,6 +6,8 @@
//

import SwiftUI
import DiscordKit
import DiscordKitCore
import CachedAsyncImage

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

struct ServerButton: View {
let selected: Bool
var guild: PreloadedGuild?
let name: String
var systemIconName: String?
var assetIconName: String?
Expand All @@ -51,6 +54,7 @@ struct ServerButton: View {
.buttonStyle(
ServerButtonStyle(
selected: selected,
guild: guild,
name: name,
bgColor: bgColor,
systemName: systemIconName,
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Up @@ -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
Expand Down