Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
532a681
0.2.6
matheusfillipe Mar 12, 2026
6291202
Merge branch 'main' of https://github.com/ObsidianIRC/ObsidianIRC
matheusfillipe Mar 13, 2026
8fb7414
Add image view modal
matheusfillipe Mar 14, 2026
6e63e43
Add share dialog in mobile
matheusfillipe Mar 14, 2026
f3ad23b
fix biome
matheusfillipe Mar 14, 2026
c7b70ec
remove download button in browser
matheusfillipe Mar 14, 2026
e9c1321
Add a filmstrip in the image viewer
matheusfillipe Mar 14, 2026
c2cef59
horizontal scroll in filmstrip
matheusfillipe Mar 14, 2026
1434728
Small fixes
matheusfillipe Mar 15, 2026
cd9eeef
Add comments section
matheusfillipe Mar 15, 2026
e109794
Add reactions, avatars, image previews in the comment section
matheusfillipe Mar 15, 2026
780b838
add image thumbnail to replies
matheusfillipe Mar 15, 2026
79f0833
enforce media trust settings in reply thumbnails and filmstrip
matheusfillipe Mar 15, 2026
35ae54f
make it resizable
matheusfillipe Mar 15, 2026
880d564
Some review feedback
matheusfillipe Mar 15, 2026
26d369c
mobile fixes
matheusfillipe Mar 15, 2026
ceaa702
Small fixes with mobile keyboard
matheusfillipe Mar 16, 2026
d1ec68a
Support more media formats and extend media viewer
matheusfillipe Mar 18, 2026
ccc5e9e
Many fixes
matheusfillipe Mar 19, 2026
ee7ff15
fix android download
matheusfillipe Mar 19, 2026
d256e3a
Auto focus input on type
matheusfillipe Mar 19, 2026
e305cf0
small fix
matheusfillipe Mar 20, 2026
d077bf3
Add agents.md
matheusfillipe Mar 20, 2026
8b4b27d
Large split of store and irc client
matheusfillipe Mar 20, 2026
6971a98
Merge branch 'main' of https://github.com/ObsidianIRC/ObsidianIRC int…
matheusfillipe Mar 20, 2026
ecb3c85
put rename in the channel settings
matheusfillipe Mar 21, 2026
ea59f54
Add slider UI for media preview control
matheusfillipe Mar 21, 2026
c8540da
add topic url previews
matheusfillipe Mar 21, 2026
abd7f6e
fixes for embeded players and pdf viewer
matheusfillipe Mar 24, 2026
66e83de
filmstrip fixes and improve reaction hover tooltip
matheusfillipe Mar 27, 2026
6b77034
Fix WKWebView chat scroll on keystroke in multiline input
matheusfillipe Mar 28, 2026
c42f1a5
any keypress should focus input
matheusfillipe Mar 28, 2026
b9f0f7f
Some fixes in chat history loading
matheusfillipe Apr 3, 2026
6417b2a
buffer chathistory messages until batch end, fix scroll position on l…
matheusfillipe Apr 3, 2026
02b56ce
Fix embed preview false positives, player error fallback, and media v…
matheusfillipe Apr 3, 2026
8cabcca
update to react 19
matheusfillipe Apr 4, 2026
2f93eeb
Player fixes
matheusfillipe Apr 4, 2026
2f72fcc
fix scroll stuff
matheusfillipe Apr 6, 2026
69bb941
Avoid flicking collapsed messages
matheusfillipe Apr 7, 2026
a73b19d
fix layout restoration and cleanup
matheusfillipe Apr 7, 2026
ee5e8d7
Fix multiple usability issues
matheusfillipe Apr 8, 2026
654874c
Multiple mobile fixes, haptics, better channel reordering
matheusfillipe Apr 8, 2026
10160a9
perf: channel switch, memo, bundle splitting, hljs lazy load
matheusfillipe Apr 8, 2026
eea8998
history aggregation fixes
matheusfillipe Apr 9, 2026
08cdfe0
restore history on reconnect
matheusfillipe Apr 9, 2026
61a9c31
Review suggesions
matheusfillipe Apr 9, 2026
659cbf9
fix ignore
matheusfillipe Apr 9, 2026
cc9eead
reply and draft are ratified
matheusfillipe Apr 9, 2026
a64019a
Attempt to fix metadtata messup
matheusfillipe Apr 10, 2026
8335887
Protect external urls on user profile, server list, channels, etc
matheusfillipe Apr 10, 2026
1ab0af2
add pronouns
matheusfillipe Apr 11, 2026
f1bdb71
show message action buttons a bit above
matheusfillipe Apr 12, 2026
e76c3ef
Fix that weird scrolling issue in ios when keyboard is shown
matheusfillipe Apr 12, 2026
241bf7c
Merge branch 'main' of https://github.com/ObsidianIRC/ObsidianIRC int…
matheusfillipe Apr 13, 2026
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
18 changes: 15 additions & 3 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ tauri-plugin-single-instance = { version = "2.0.0", features = ["deep-link"] }
rfd = { version = "0.16", features = ["tokio"] }

[target.'cfg(target_os = "ios")'.dependencies]
tauri-plugin-ios-keyboard = "0.1"
tauri-plugin-ios-keyboard = { path = "plugins/ios-keyboard" }
tauri-plugin-share-sheet = { path = "plugins/share-sheet" }

[target.'cfg(any(target_os = "ios", target_os = "android"))'.dependencies]
Expand Down
10 changes: 10 additions & 0 deletions src-tauri/plugins/ios-keyboard/.tauri/tauri-api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
Package.resolved
40 changes: 40 additions & 0 deletions src-tauri/plugins/ios-keyboard/.tauri/tauri-api/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// swift-tools-version:5.3
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

import PackageDescription

let package = Package(
name: "Tauri",
platforms: [
.macOS(.v10_13),
.iOS(.v11),
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "Tauri",
type: .static,
targets: ["Tauri"])
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(name: "SwiftRs", url: "https://github.com/Brendonovich/swift-rs", from: "1.0.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "Tauri",
dependencies: [
.byName(name: "SwiftRs")
],
path: "Sources"
),
.testTarget(
name: "TauriTests",
dependencies: ["Tauri"]
),
]
)
3 changes: 3 additions & 0 deletions src-tauri/plugins/ios-keyboard/.tauri/tauri-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Tauri

Tauri iOS API.
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

import Foundation

let CHANNEL_PREFIX = "__CHANNEL__:"
let channelDataKey = CodingUserInfoKey(rawValue: "sendChannelData")!

public class Channel: Decodable {
public let id: UInt64
let handler: (UInt64, String) -> Void

public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let channelDef = try container.decode(String.self)

let components = channelDef.components(separatedBy: CHANNEL_PREFIX)
if components.count < 2 {
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "Invalid channel definition from \(channelDef)"
)

}
guard let channelId = UInt64(components[1]) else {
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "Invalid channel ID from \(channelDef)"
)
}

guard let handler = decoder.userInfo[channelDataKey] as? (UInt64, String) -> Void else {
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "missing userInfo for Channel handler. This is a Tauri issue"
)
}

self.id = channelId
self.handler = handler
}

func serialize(_ data: JsonValue) -> String {
do {
return try data.jsonRepresentation() ?? "\"Failed to serialize payload\""
} catch {
return "\"\(error)\""
}
}

public func send(_ data: JsonObject) {
send(.dictionary(data))
}

public func send(_ data: JsonValue) {
handler(id, serialize(data))
}

public func send<T: Encodable>(_ data: T) throws {
let json = try JSONEncoder().encode(data)
handler(id, String(decoding: json, as: UTF8.self))
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

import Foundation
import UIKit

@objc public class Invoke: NSObject {
public let command: String
let callback: UInt64
let error: UInt64
let data: String
let sendResponse: (UInt64, String?) -> Void
let sendChannelData: (UInt64, String) -> Void

public init(
command: String, callback: UInt64, error: UInt64,
sendResponse: @escaping (UInt64, String?) -> Void,
sendChannelData: @escaping (UInt64, String) -> Void, data: String
) {
self.command = command
self.callback = callback
self.error = error
self.data = data
self.sendResponse = sendResponse
self.sendChannelData = sendChannelData
}

public func getRawArgs() -> String {
return self.data
}

public func getArgs() throws -> JSObject {
let jsonData = self.data.data(using: .utf8)!
let data = try JSONSerialization.jsonObject(with: jsonData, options: [])
return JSTypes.coerceDictionaryToJSObject(
(data as! NSDictionary), formattingDatesAsStrings: true)!
}

public func parseArgs<T: Decodable>(_ type: T.Type) throws -> T {
let jsonData = self.data.data(using: .utf8)!
let decoder = JSONDecoder()
decoder.userInfo[channelDataKey] = sendChannelData
return try decoder.decode(type, from: jsonData)
}

func serialize(_ data: JsonValue) -> String {
do {
return try data.jsonRepresentation() ?? "\"Failed to serialize payload\""
} catch {
return "\"\(error)\""
}
}

public func resolve() {
sendResponse(callback, nil)
}

public func resolve(_ data: JsonObject) {
resolve(.dictionary(data))
}

public func resolve(_ data: JsonValue) {
sendResponse(callback, serialize(data))
}

public func resolve<T: Encodable>(_ data: T) {
do {
let json = try JSONEncoder().encode(data)
sendResponse(callback, String(decoding: json, as: UTF8.self))
} catch {
sendResponse(self.error, "\"\(error)\"")
}
}

public func reject(
_ message: String, code: String? = nil, error: Error? = nil, data: JsonValue? = nil
) {
let payload: NSMutableDictionary = [
"message": message
]

if let code = code {
payload["code"] = code
}

if let error = error {
payload["error"] = error
}

if let data = data {
switch data {
case .dictionary(let dict):
for entry in dict {
payload[entry.key] = entry.value
}
}
}

sendResponse(self.error, serialize(.dictionary(payload as! JsonObject)))
}

public func unimplemented() {
unimplemented("not implemented")
}

public func unimplemented(_ message: String) {
reject(message)
}

public func unavailable() {
unavailable("not available")
}

public func unavailable(_ message: String) {
reject(message)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

import Foundation

// declare our empty protocol, and conformance, for typing
public protocol JSValue {}
extension String: JSValue {}
extension Bool: JSValue {}
extension Int: JSValue {}
extension Float: JSValue {}
extension Double: JSValue {}
extension NSNumber: JSValue {}
extension NSNull: JSValue {}
extension Array: JSValue {}
extension Date: JSValue {}
extension Dictionary: JSValue where Key == String, Value == JSValue {}

// convenience aliases
public typealias JSObject = [String: JSValue]
public typealias JSArray = [JSValue]

extension Dictionary where Key == String, Value == JSValue {
public func getValue(_ key: String) -> JSValue? {
return self[key]
}

public func getString(_ key: String) -> String? {
return self[key] as? String
}

public func getBool(_ key: String) -> Bool? {
return self[key] as? Bool
}

public func getInt(_ key: String) -> Int? {
return self[key] as? Int
}

public func getFloat(_ key: String) -> Float? {
if let floatValue = self[key] as? Float {
return floatValue
} else if let doubleValue = self[key] as? Double {
return Float(doubleValue)
}
return nil
}

public func getDouble(_ key: String) -> Double? {
return self[key] as? Double
}

public func getArray(_ key: String) -> JSArray? {
return self[key] as? JSArray
}

public func getObject(_ key: String) -> JSObject? {
return self[key] as? JSObject
}
}

/*
Simply casting objects from foundation class clusters (such as __NSArrayM)
doesn't work with the JSValue protocol and will always fail. So we need to
recursively and explicitly convert each value in the dictionary.
*/
public enum JSTypes {}
extension JSTypes {
public static func coerceDictionaryToJSObject(
_ dictionary: NSDictionary?, formattingDatesAsStrings: Bool = false
) -> JSObject? {
return coerceToJSValue(dictionary, formattingDates: formattingDatesAsStrings) as? JSObject
}

public static func coerceDictionaryToJSObject(
_ dictionary: [AnyHashable: Any]?, formattingDatesAsStrings: Bool = false
) -> JSObject? {
return coerceToJSValue(dictionary, formattingDates: formattingDatesAsStrings) as? JSObject
}
}

private let dateStringFormatter = ISO8601DateFormatter()

// We need a large switch statement because we have a lot of types.
// swiftlint:disable:next cyclomatic_complexity
private func coerceToJSValue(_ value: Any?, formattingDates: Bool) -> JSValue? {
guard let value = value else {
return nil
}
switch value {
case let stringValue as String:
return stringValue
case let numberValue as NSNumber:
return numberValue
case let boolValue as Bool:
return boolValue
case let intValue as Int:
return intValue
case let floatValue as Float:
return floatValue
case let doubleValue as Double:
return doubleValue
case let dateValue as Date:
if formattingDates {
return dateStringFormatter.string(from: dateValue)
}
return dateValue
case let nullValue as NSNull:
return nullValue
case let arrayValue as NSArray:
return arrayValue.compactMap { coerceToJSValue($0, formattingDates: formattingDates) }
case let dictionaryValue as NSDictionary:
let keys = dictionaryValue.allKeys.compactMap { $0 as? String }
var result: JSObject = [:]
for key in keys {
result[key] = coerceToJSValue(dictionaryValue[key], formattingDates: formattingDates)
}
return result
default:
return nil
}
}
Loading
Loading