Coder Social home page Coder Social logo

dimillian / icecubesapp Goto Github PK

View Code? Open in Web Editor NEW
4.8K 40.0 454.0 88.15 MB

A SwiftUI Mastodon client

Home Page: https://apps.apple.com/us/app/ice-cubes-for-mastodon/id6444915884

License: GNU Affero General Public License v3.0

Swift 99.93% Shell 0.02% JavaScript 0.05%
ios swift swiftui mastodon

icecubesapp's Introduction

IceCubesApp

Download on the App Store

IceCubesApp is an open-source application for accessing the decentralized social network Mastodon! It's built entirely in SwiftUI, making it fast, lightweight, and easy to use.

You can connect to any Mastodon instance, browse your timeline, interact with other users, and post updates and media.

It's multiplatform and works on iOS, macOS, iPadOS, and visionOS. It has a dedicated UI with a sidebar on macOS and iPadOS.

Features

Timeline

  • A navigation bar title menu lets you easily swap between your home, local, federated, and trending timeline.
  • You can also easily access your lists, followed tags, and tag groups.
  • Tag groups are custom timelines made of multiple tags, a feature unique to Ice Cubes.
  • Quote post!
  • You can also add a remote local timeline. A helpful feature to browse the public timeline of other instances. Another Ice Cubes only feature.
  • Ice Cubes relies heavily on the streaming events of Mastodon to do stuff like showing new posts live in the home timeline and editing and deleting your posts.
  • The timeline sync is semi-automatic; your position is sent to the Mastodon marker API, and from another device running Ice Cubes, you can resume your home timeline position.
  • The home timeline is cached using the third-party library Bodega. A lite SQLite wrappers. The current position is saved in user default, so when you switch accounts or launch the app, your cached home timeline and position are restored. Then new posts will be fetched and displayed with an unread counter.
  • iCloud sync of tag groups, remote timelines and drafts.
  • Server side filters support.
  • Create and switch to your lists.

Code -> Status & Timeline package

Editor / Composer

  • Full-featured post editor.
  • You can write threads up to 5 posts.
  • Upload up to 4 images.
  • AI-assisted tools using OpenAI API for text correction, hashtag generation, and more.
  • Generate image description using AI!
  • Custom emojis support.
  • Add polls and content warnings.
  • Save/restore from drafts.
  • Use the Apple language detection feature to suggest the language before posting.

Code -> Status package -> StatusEditor component Code -> OpenAIClient

Notifications

  • Full support for push notifications.
  • Ice Cubes runs its proxy between Mastodon and APNS, which is necessary for Mastodon to route push notifications to the user's device.
  • Push notifications are never and can't be read by the proxy.
  • Push notifications content/body is decoded on the device. Look for NotificationServiceSupport
  • Push notifications are rich, and use Apple INSendMessageIntent API for contact pictures.
  • Push notifications are grouped by activities, like mentions, favorites, boosts, etc...
  • Notifications within the app are also grouped/stacked.
  • You can select which kind of push notifications you want to receive within the app.
  • Route to the correct post and switch to the proper account when taping notifications.

Code -> Notifications package and NotificationService extension.

Explore / Search

  • Dedicated explore/search tab for trending users, tags, posts, and links.
  • Easy access to all those categories from the top area.
  • You can search for everything or filter by users, tags, and posts.
  • See a graph for the activities on the trending tags.
  • See more from each category to access the full-screen view.

Code -> Explore package

Direct Messages

  • Dedicated tab for direct/private messages.
  • Chat like UI
  • You can use the inline composer for a quick chat or the full editor.

Code -> Conversations package

Profile

  • Rich profile support.
  • Fully access & tweak your privacy settings.
  • Edit your profile, from your bio to your custom fields.
  • Easy access to Mastometrics.
  • Add a custom server-side note visible only to you for any profile.
  • Translate the bio of any profile to your language.
  • Easy access to edit your server-side timeline filters.
  • Easy access to share your profile outside of the app.

Code -> Account package

Multi Accounts

  • Support for an unlimited number of Mastodon accounts.
  • Easily browse/discover instances and log in!
  • Swap account from any screen on iOS.
  • Swap your account from the macOS/iPadOS sidebar with just one click.
  • Full server-side and client-side account settings support.
  • Swipe to delete an account from the settings.
  • Safe authentication using Apple's WebAuthenticationSession
  • The account token is securely stored in the keychain.

Code -> Account & AppAcount packages

Other

  • You can support this project with tips in the app and Github sponsoring.
  • Massive amount of customization settings.
  • You can tweak the display settings like the font, line spacing, status actions buttons, etc...
  • Many built-in themes.
  • Make your theme.
  • Customize your swipe gestures.
  • Customize your tabbar entries.
  • Sound & haptic feedback support.

A note on the architecture

The project is split into different Swift Packages to make managing and maintaining the codebase easier. Each package focuses on a specific application aspect, such as the UI, network communication, or data models. This modular approach allows for easier collaboration and ensures the code is organized and easily understood.

It's a great starting point for learning SwiftUI. The app covers many of the basic concepts of SwiftUI, such as building layouts, working with data, and handling user interaction. By exploring the code, you can understand how to use SwiftUI in your daily life. Plus, the open-source nature of IceCubesApp means you can see how real-world applications are built and get a sense of best practices for using SwiftUI.

The architecture is straightforward MVVM for most parts, there is no redux on this one ;)

Thanks!

Building the project

To build the project, you need to clone the repo and create a copy of the included .xcconfig file to create your config before you can compile the project. Otherwise, you will get an error.

Here are the steps:

  1. Clone the repo
  2. In the same folder that contains the IceCubesApp.xcconfig.template, run this command:
cp IceCubesApp.xcconfig.template IceCubesApp.xcconfig
  1. Fill in the DEVELOPMENT_TEAM and BUNDLE_ID_PREFIX values. The first should have your Apple Team ID (which you can find by logging into the Apple Developer Portal). The latter is your domain in reverse notation or whatever you use as the prefix for your projects.
  2. Save your changes, and then you should be able to compile the project without any issues.

icecubesapp's People

Contributors

andrzejrozga avatar dean151 avatar dertuxmalwieder avatar dimillian avatar divadretlaw avatar egesucu avatar enetter avatar evilone avatar havhingstor avatar icod avatar jairhenrique avatar jdanthinne avatar jerry23011 avatar jesusjimsa avatar kean avatar kitwtnb avatar kovalyshyn avatar kum4423 avatar mofmofmofneko avatar nesevis avatar nixzhu avatar rbnval avatar sh95014 avatar sothawo avatar thai-d-v avatar torre76 avatar vollkorntomate avatar wedgesparda avatar xabirequejo avatar xurble avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

icecubesapp's Issues

Noticeable lag when typing search term

This is not a bug, but rather a UX improvement request. There is a noticeable delay when typing the search term and the caret is jumping behind the last character. I noticed this on the Mac app running on Apple Silicon.

Screen.Recording.2023-01-17.at.18.20.50.mov

Feature request: automatic theme switching

Currently, you can choose from a list of themes globally.
It would be nice to pick a theme for light mode, a theme for dark mode, and a setting to choose between always light, always dark or automatic theme switching (following the system behavior).

[Bug] Search is not working properly for me

I am having a lot of issues with the search feature. I tried to get as many videos as I could.

The app seems to crash a lot for me when loading search results:
https://user-images.githubusercontent.com/91493312/212547402-db4e13dc-c037-48b8-8dcb-a6bee56c0281.MOV

Sometimes when I type the cursor position randomly changes:
https://user-images.githubusercontent.com/91493312/212547441-10810e2b-480c-4b59-af83-79d9393cac79.MOV

Not sure what the issue is with this one, but the Japanese keyboard doesn't seem to be working properly. I haven't had this happen anywhere else. But it does not display autocomplete suggestions, which is really important for Japanese.
https://user-images.githubusercontent.com/91493312/212547548-91ef870d-c433-44bd-81af-ce280023d3e8.MOV

Feature request: customizable text colours, and themes

This is a really great app already, congratulations! I’d love to see the ability to customize the default text, and with that being able to save, and import/export themes. I’ve already added some Nord colours to mine, so it would be great to bring more of the swatches in, as well as share them back out with the community.

[Feature request] Proper threading view

Hey there, I really would like to see a proper thread view. For some inspiration you could check Woolly or Tusker. They seem to do a pretty good job at it.

Dark theme background colors are flipped

When Nemesis Light Mode is selected, the background color of posts and pressable menu items in the settings screen are rendered #ffffff, with their surroundings rendered off-white. This is expected behavior. However, in Nemesis Dark Mode, the primary and secondary background colors don't follow expected behavior for Apple's theming guidelines. Specifically, the pressable menu items in the settings page should be #1c1c1e, and the color of the background view should be #000000, with the color of posts matching the background view's color this time around.

A good example for how this should look is the Settings app:

Settings (Light) Settings (Dark)
IMG_1558 IMG_1559

Compare this with Ice Cubes's theming method:

Ice Cubes (Light) Ice Cubes (Dark)
IMG_1570 IMG_1571

Continuing on this, I would also love to see a "default iOS look" theme; given that the app is written using SwiftUI and greatly matches Apple's own design aesthetic, I think following Apple's Foundations colors would make a lot of sense. I assume this could easily be added as two new theme options. More specifically, for the light theme, background color is #ffffff, secondary background color is f2f2f7, links are #007aff, and for the dark theme, background color is #000000, secondary background color is #1c1c1e, and links are #0A84ff. That said, it might be cool to see two dark modes, one regular and another AMOLED, as well.

Custom tint color didn't persist?

Changed the Tint color before updating the app. And updated the app from testflight. Now it defaults to previous default color not the one i selected

Glitch with the Visibility button when the keyboard moves

The Visibility menu button is being vertically offset after the keyboard has moved at least once.

This is easily reproducible in simulator by toggling the hardware keyboard on and off (see attached video), but also happens on device after you leave the photo selector screen.

Looking at the code the culprit might be the fact that it isn't a regular SwiftUI button but a Menu, and something strange definitely happens within the convoluted view hierarchy SwiftUI creates for that.

Simulator.Screen.Recording.-.iPhone.14.-.2023-01-10.at.14.37.38.mp4

Double tap to "confirm" dialog when favoriting/boosting a post

Liking Icecube a lot so far after having tried several other Mastodon apps. My only feedback would be to ask for an optional "confirm" dialog when favoriting/boosting a post. Perhaps an option to require a double tap to favorite/boost so I don't hit it accidentally? Thank you for the work so far, can't wait for the public release!

Blur sensitive images with an option to auto expand them

  Having the option to blur images marked as sensitive. I've seen a lot of ways to implement it design-wise. Some apps have a little eye icon over the image to toggle it. I think a couple of them just blur the image until you tap on it and then blur it again when you swipe the image away.  Both designs seem cool to me. I'm not sure what design would be best, though.

Originally posted by @btylerh7 in https://github.com/Dimillian/IceCubesApp/discussions/41#discussioncomment-4636334

Graphic glitch while switching tab

There is this graphic glitch while switching tab in Notifications. It also appeared when Notifications was still empty
iPhone 11 Pro iOS 16.2
Version 0.7 build 712 (was also present on the original build)

RPReplay_Final1672434980.mp4

Scrolling past (muted) video stops system audio

If you e.g. listen to Music or a Podcast and scroll past a video in your timeline audio stops. Ice Cubes should probably use AVAudioSession.mixWithOthers while autoplaying a muted video.

Would you please use some code format tool like SwiftFormat to format the code?

Would you please use some code format tool like SwiftFormat to format the code?

Before format

import SwiftUI
import Network
import Models
import Shimmer
import Status
import DesignSystem
import Env

public struct TimelineView: View {
  private enum Constants {
    static let scrollToTop = "top"
  }
  
  @Environment(\.scenePhase) private var scenePhase
  @EnvironmentObject private var theme: Theme
  @EnvironmentObject private var account: CurrentAccount
  @EnvironmentObject private var watcher: StreamWatcher
  @EnvironmentObject private var client: Client
  @EnvironmentObject private var routerPath: RouterPath
  
  @StateObject private var viewModel = TimelineViewModel()

  @State private var scrollProxy: ScrollViewProxy?
  @Binding var timeline: TimelineFilter
  @Binding var scrollToTopSignal: Int
  
  private let feedbackGenerator = UIImpactFeedbackGenerator()
  
  public init(timeline: Binding<TimelineFilter>, scrollToTopSignal: Binding<Int>) {
    _timeline = timeline
    _scrollToTopSignal = scrollToTopSignal
  }
  
  public var body: some View {
    ScrollViewReader { proxy in
      ZStack(alignment: .top) {
        ScrollView {
          Rectangle()
            .frame(height: 0)
            .id(Constants.scrollToTop)
          LazyVStack {
            tagHeaderView
              .padding(.bottom, 16)
            StatusesListView(fetcher: viewModel)
          }
          .padding(.top, .layoutPadding)
        }
        .background(theme.primaryBackgroundColor)
        if viewModel.pendingStatusesEnabled {
          makePendingNewPostsView(proxy: proxy)
        }
      }
      .onAppear {
        scrollProxy = proxy
      }
    }
    .navigationTitle(timeline.title())
    .toolbar{
      switch timeline {
      case let .list(list):
        ToolbarItem {
          Button {
            routerPath.presentedSheet = .listEdit(list: list)
          } label: {
            Image(systemName: "pencil")
          }
        }
      default:
        ToolbarItem {
          EmptyView()
        }
      }
    }
    .navigationBarTitleDisplayMode(.inline)
    .onAppear {
      viewModel.client = client
      viewModel.timeline = timeline
    }
    .refreshable {
      feedbackGenerator.impactOccurred(intensity: 0.3)
      await viewModel.fetchStatuses(userIntent: true)
      feedbackGenerator.impactOccurred(intensity: 0.7)
    }
    .onChange(of: watcher.latestEvent?.id) { id in
      if let latestEvent = watcher.latestEvent {
        viewModel.handleEvent(event: latestEvent, currentAccount: account)
      }
    }
    .onChange(of: scrollToTopSignal, perform: { _ in
      withAnimation {
        scrollProxy?.scrollTo(Constants.scrollToTop, anchor: .top)
      }
    })
    .onChange(of: timeline) { newTimeline in
      viewModel.timeline = timeline
    }
    .onChange(of: scenePhase, perform: { scenePhase in
      switch scenePhase {
      case .active:
        Task {
          await viewModel.fetchStatuses(userIntent: false)
        }
      default:
        break
      }
    })
  }
  
  @ViewBuilder
  private func makePendingNewPostsView(proxy: ScrollViewProxy) -> some View {
    if !viewModel.pendingStatuses.isEmpty {
      HStack(spacing: 6) {
        Button {
          withAnimation {
            proxy.scrollTo(Constants.scrollToTop)
            viewModel.displayPendingStatuses()
          }
        } label: {
          Text(viewModel.pendingStatusesButtonTitle)
        }
        .buttonStyle(.bordered)
        .background(.thinMaterial)
        .cornerRadius(8)
        if viewModel.pendingStatuses.count > 1 {
          Button {
            withAnimation {
              viewModel.dequeuePendingStatuses()
            }
          } label: {
            Image(systemName: "play.square.stack")
          }
          .buttonStyle(.bordered)
          .background(.thinMaterial)
          .cornerRadius(8)
        }
      }
      .padding(.top, 6)
    }
  }
  
  @ViewBuilder
  private var tagHeaderView: some View {
    if let tag = viewModel.tag {
      HStack {
        VStack(alignment: .leading, spacing: 4) {
          Text("#\(tag.name)")
            .font(.headline)
          Text("\(tag.totalUses) recent posts from \(tag.totalAccounts) participants")
            .font(.footnote)
            .foregroundColor(.gray)
        }
        Spacer()
        Button {
          Task {
            if tag.following {
              viewModel.tag = await account.unfollowTag(id: tag.name)
            } else {
              viewModel.tag = await account.followTag(id: tag.name)
            }
          }
        } label: {
          Text(tag.following ? "Following": "Follow")
        }.buttonStyle(.bordered)
      }
      .padding(.horizontal, .layoutPadding)
      .padding(.vertical, 8)
      .background(theme.secondaryBackgroundColor)
    }
  }
}

After format

import DesignSystem
import Env
import Models
import Network
import Shimmer
import Status
import SwiftUI

public struct TimelineView: View {
    private enum Constants {
        static let scrollToTop = "top"
    }
  
    @Environment(\.scenePhase) private var scenePhase
    @EnvironmentObject private var theme: Theme
    @EnvironmentObject private var account: CurrentAccount
    @EnvironmentObject private var watcher: StreamWatcher
    @EnvironmentObject private var client: Client
    @EnvironmentObject private var routerPath: RouterPath
  
    @StateObject private var viewModel = TimelineViewModel()

    @State private var scrollProxy: ScrollViewProxy?
    @Binding var timeline: TimelineFilter
    @Binding var scrollToTopSignal: Int
  
    private let feedbackGenerator = UIImpactFeedbackGenerator()
  
    public init(timeline: Binding<TimelineFilter>, scrollToTopSignal: Binding<Int>) {
        _timeline = timeline
        _scrollToTopSignal = scrollToTopSignal
    }
  
    public var body: some View {
        ScrollViewReader { proxy in
            ZStack(alignment: .top) {
                ScrollView {
                    Rectangle()
                        .frame(height: 0)
                        .id(Constants.scrollToTop)
                    LazyVStack {
                        tagHeaderView
                            .padding(.bottom, 16)
                        StatusesListView(fetcher: viewModel)
                    }
                    .padding(.top, .layoutPadding)
                }
                .background(theme.primaryBackgroundColor)
                if viewModel.pendingStatusesEnabled {
                    makePendingNewPostsView(proxy: proxy)
                }
            }
            .onAppear {
                scrollProxy = proxy
            }
        }
        .navigationTitle(timeline.title())
        .toolbar {
            switch timeline {
            case let .list(list):
                ToolbarItem {
                    Button {
                        routerPath.presentedSheet = .listEdit(list: list)
                    } label: {
                        Image(systemName: "pencil")
                    }
                }
            default:
                ToolbarItem {
                    EmptyView()
                }
            }
        }
        .navigationBarTitleDisplayMode(.inline)
        .onAppear {
            viewModel.client = client
            viewModel.timeline = timeline
        }
        .refreshable {
            feedbackGenerator.impactOccurred(intensity: 0.3)
            await viewModel.fetchStatuses(userIntent: true)
            feedbackGenerator.impactOccurred(intensity: 0.7)
        }
        .onChange(of: watcher.latestEvent?.id) { _ in
            if let latestEvent = watcher.latestEvent {
                viewModel.handleEvent(event: latestEvent, currentAccount: account)
            }
        }
        .onChange(of: scrollToTopSignal, perform: { _ in
            withAnimation {
                scrollProxy?.scrollTo(Constants.scrollToTop, anchor: .top)
            }
        })
        .onChange(of: timeline) { _ in
            viewModel.timeline = timeline
        }
        .onChange(of: scenePhase, perform: { scenePhase in
            switch scenePhase {
            case .active:
                Task {
                    await viewModel.fetchStatuses(userIntent: false)
                }
            default:
                break
            }
        })
    }
  
    @ViewBuilder
    private func makePendingNewPostsView(proxy: ScrollViewProxy) -> some View {
        if !viewModel.pendingStatuses.isEmpty {
            HStack(spacing: 6) {
                Button {
                    withAnimation {
                        proxy.scrollTo(Constants.scrollToTop)
                        viewModel.displayPendingStatuses()
                    }
                } label: {
                    Text(viewModel.pendingStatusesButtonTitle)
                }
                .buttonStyle(.bordered)
                .background(.thinMaterial)
                .cornerRadius(8)
                if viewModel.pendingStatuses.count > 1 {
                    Button {
                        withAnimation {
                            viewModel.dequeuePendingStatuses()
                        }
                    } label: {
                        Image(systemName: "play.square.stack")
                    }
                    .buttonStyle(.bordered)
                    .background(.thinMaterial)
                    .cornerRadius(8)
                }
            }
            .padding(.top, 6)
        }
    }
  
    @ViewBuilder
    private var tagHeaderView: some View {
        if let tag = viewModel.tag {
            HStack {
                VStack(alignment: .leading, spacing: 4) {
                    Text("#\(tag.name)")
                        .font(.headline)
                    Text("\(tag.totalUses) recent posts from \(tag.totalAccounts) participants")
                        .font(.footnote)
                        .foregroundColor(.gray)
                }
                Spacer()
                Button {
                    Task {
                        if tag.following {
                            viewModel.tag = await account.unfollowTag(id: tag.name)
                        } else {
                            viewModel.tag = await account.followTag(id: tag.name)
                        }
                    }
                } label: {
                    Text(tag.following ? "Following" : "Follow")
                }.buttonStyle(.bordered)
            }
            .padding(.horizontal, .layoutPadding)
            .padding(.vertical, 8)
            .background(theme.secondaryBackgroundColor)
        }
    }
}

Swiping down while composing tweet discards input

It's easy to accidentally discard a post you are composing by swiping down. Ideally this state should be restored if the compose view is opened again. Alternatively a warning alert that the user is about to do a destructive action could be presented.

Multiple Account Management - Add discerning labels/avatars to selection list dropdown

When you have multiple accounts with the same name there is no way to discern which account is which.

Considering adding one or more of the following to assist in the selection process:

IMG_0912

Good luck with the rollout! This definitely isn't a show stopper, just had a minute and thought I'd submit it.

Run as a macOS app

Hello, how can I run the project targeted on Mac? It seems the IceCubesApp target doesn't support Mac.

Scrolling up sometimes gets stuck

App Version 0.7.711
iOS 16.2
iPhone 14 pro Max

With the latest app I could not scroll up for some reason. I was able to get around it when I tapped or double tapped the timeline icon.

RPReplay_Final1672487093.mp4

Toolbar Buttons on Account Details View not always readable

Currently the Back Button and the ellipsis/more/menu is not always readable, depending on the account header.

Now

One way of dealing with that would be copying what Twitter does

Twitter

Image(systemName: "ellipsis")
  .imageScale(.small)
  .foregroundColor(.white)
  .font(.body.weight(.semibold))
  .frame(width: 30, height: 30)
  .background(Color.black.opacity(0.5))
  .clipShape(Circle())

or similar but better integrated into the theme of the app.

Overriding the system back button with .navigationBarBackButtonHidden(true) disables the swipe-back gesture, but that can be re-enabled via UIKit, like this

extension UINavigationController: UIGestureRecognizerDelegate {
    override open func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
    }

    public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return viewControllers.count > 1
    }
}

but maybe there are better/different solutions to this issue

[Feature Request] Localization

Set up a system for localizing the app for languages other than English.

This will probably require at least the following points:

  • Convert all hard-coded English text in the views etc. to localization keys
  • Add the Localizable.strings file for English
  • Possibly set up an easy-to-use collaboration tool (such as Crowdin)
  • Adapt UI for right-to-left languages (I assume most of it is handled automatically by SwiftUI)

I volunteer to help setting all of this up and providing translations for German. It is just a very big change in many files and some work beyond that, so I didn't want to just open a PR.

[Feature request] Option to select post's language in the composer

It is important for multi-language people to indicate the language of their post.

The /api/v1/statuses endpoint accepts a language parameter. Its value should be a ISO 639 language code that represents the language of a status update. On the official Mastodon UI, this can be selected using the language icon.

IMG_0174

Ice Cubes could have a similar button in the New Post composer. It could be placed between the CW and Drafts buttons on the toolbar. The default language could be derived from the system language.

[Bug] macOS app crashes when pasting a link in compose screen

When I paste any link in the compose field, the macOS app crashes after some seconds. I can reproduce it all the time.

Here is the backtrace:

Last Exception Backtrace:
0   CoreFoundation                	       0x18ca533ec __exceptionPreprocess + 164
1   libobjc.A.dylib               	       0x18c59eea8 objc_exception_throw + 60
2   Foundation                    	       0x18d8a0750 -[NSXPCEncoder _checkObject:] + 308
3   Foundation                    	       0x18d8a0424 -[NSXPCEncoder _encodeUnkeyedObject:] + 40
4   Foundation                    	       0x18d8b224c -[NSXPCEncoder _encodeArrayOfObjects:forKey:] + 184
5   Foundation                    	       0x18d8cd8d0 -[NSDictionary(NSDictionary) encodeWithCoder:] + 580
6   Foundation                    	       0x18d8a0a7c -[NSXPCEncoder _encodeObject:] + 488
7   Foundation                    	       0x18d8b224c -[NSXPCEncoder _encodeArrayOfObjects:forKey:] + 184
8   Foundation                    	       0x18d8bd81c -[NSArray(NSArray) encodeWithCoder:] + 588
9   Foundation                    	       0x18d8a0a7c -[NSXPCEncoder _encodeObject:] + 488
10  Foundation                    	       0x18d971a24 -[NSAttributedString encodeWithCoder:] + 928
11  Foundation                    	       0x18d8a0a7c -[NSXPCEncoder _encodeObject:] + 488
12  RemoteTextInput               	       0x1a76b6ccc -[RTIDocumentState encodeWithCoder:] + 464
13  Foundation                    	       0x18d8a0a7c -[NSXPCEncoder _encodeObject:] + 488
14  Foundation                    	       0x18d8a575c _NSXPCSerializationAddInvocationWithOnlyObjectArgumentsArray + 116
15  Foundation                    	       0x18d8a5614 -[NSXPCEncoder _encodeInvocationObjectArgumentsOnly:count:typeString:selector:isReply:into:] + 212
16  Foundation                    	       0x18d89ee58 -[NSXPCConnection _sendInvocation:orArguments:count:methodSignature:selector:withProxy:] + 1264
17  Foundation                    	       0x18d8a5504 -[NSXPCConnection _sendSelector:withProxy:arg1:arg2:] + 128
18  Foundation                    	       0x18d8a542c _NSXPCDistantObjectSimpleMessageSend2 + 68
19  RemoteTextInput               	       0x1a76bb108 __52-[RTIInputSystemClient _updateTextForSessionWithID:]_block_invoke + 72
20  RemoteTextInput               	       0x1a76bac8c __58-[RTIInputSystemClient enumerateServices:force:withBlock:]_block_invoke.131 + 60
21  CoreFoundation                	       0x18ca0ad48 __NSSET_IS_CALLING_OUT_TO_A_BLOCK__ + 24
22  CoreFoundation                	       0x18ca23bc4 -[__NSSetM enumerateObjectsWithOptions:usingBlock:] + 200
23  RemoteTextInput               	       0x1a76bab5c -[RTIInputSystemClient enumerateServices:force:withBlock:] + 292
24  RemoteTextInput               	       0x1a76bb08c -[RTIInputSystemClient _updateTextForSessionWithID:] + 244
25  RemoteTextInput               	       0x1a76bbeec -[RTIInputSystemClient remoteTextInputSessionWithID:documentDidChange:] + 140
26  UIKitCore                     	       0x1b73df6d8 __39-[UIKBRTIPartner documentStateChanged:]_block_invoke + 124
27  UIKitCore                     	       0x1b73e39f8 __48-[UIKBRTIPartner _updateRTIStateWithCompletion:]_block_invoke + 300
28  UIKitCore                     	       0x1b73e94d8 -[UIKBRTIPartner _queryUIKitDocumentRequest:completion:] + 2792
29  UIKitCore                     	       0x1b73e7a3c -[UIKBRTIPartner _queryDocumentRequest:completion:] + 120
30  UIKitCore                     	       0x1b73e3888 -[UIKBRTIPartner _updateRTIStateWithCompletion:] + 444
31  UIKitCore                     	       0x1b73e2a3c -[UIKBRTIPartner updateStateWithCompletion:] + 52
32  UIKitCore                     	       0x1b73df648 -[UIKBRTIPartner documentStateChanged:] + 140
33  UIKitCore                     	       0x1b73612ec -[UIKeyboardImpl setDocumentState:] + 208
34  UIKitCore                     	       0x1b7375e8c -[UIKeyboardImpl updateKeyboardStateForInsertion:] + 104
35  UIKitCore                     	       0x1b70f9278 -[UIKBInputDelegateManager insertText:updateInputSource:] + 384
36  UIKitCore                     	       0x1b70f722c -[UIKBInputDelegateManager insertText:] + 72
37  UIKitCore                     	       0x1b736da00 -[UIKeyboardImpl performKeyboardOutput:checkingDelegate:forwardToRemoteInputSource:] + 2596
38  UIKit                         	       0x2171f55b4 -[UIKeyboardImplAccessibility performKeyboardOutput:checkingDelegate:forwardToRemoteInputSource:] + 88
39  UIKitCore                     	       0x1b73e76f0 -[UIKBRTIPartner _performKeyboardOutputOperations:] + 416
40  UIKitCore                     	       0x1b73e68c8 -[UIKBRTIPartner _queued_performTextOperations:resultHandler:] + 592
41  libdispatch.dylib             	       0x18c7709dc _dispatch_call_block_and_release + 32
42  libdispatch.dylib             	       0x18c772504 _dispatch_client_callout + 20
43  libdispatch.dylib             	       0x18c780d1c _dispatch_main_queue_drain + 928
44  libdispatch.dylib             	       0x18c78096c _dispatch_main_queue_callback_4CF + 44
45  CoreFoundation                	       0x18ca19d50 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
46  CoreFoundation                	       0x18c9d77d0 __CFRunLoopRun + 2036
47  CoreFoundation                	       0x18c9d6888 CFRunLoopRunSpecific + 612
48  HIToolbox                     	       0x1960abfa0 RunCurrentEventLoopInMode + 292
49  HIToolbox                     	       0x1960abc30 ReceiveNextEventCommon + 236
50  HIToolbox                     	       0x1960abb2c _BlockUntilNextEventMatchingListInModeWithFilter + 72
51  AppKit                        	       0x18fc58424 _DPSNextEvent + 632
52  AppKit                        	       0x18fc575b4 -[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 728
53  AppKit                        	       0x18fc4b9e4 -[NSApplication run] + 464
54  AppKit                        	       0x18fc22e28 NSApplicationMain + 880
55  AppKit                        	       0x18fe7ca44 +[NSWindow _savedFrameFromString:] + 0
56  UIKitMacHelper                	       0x1a2f0aba8 UINSApplicationMain + 988
57  UIKitCore                     	       0x1b69a0934 UIApplicationMain + 148
58  SwiftUI                       	       0x1cd6f8b4c 0x1cc505000 + 18824012
59  SwiftUI                       	       0x1cd6f8998 0x1cc505000 + 18823576
60  SwiftUI                       	       0x1cccd0c08 0x1cc505000 + 8174600
61  IceCubesApp                   	       0x1026dd378 0x1026d4000 + 37752
62  dyld                          	       0x18c5cfe50 start + 2544

Support twitter link

I see a lot of people doing [email protected], when you taps these links the app will crash, maybe we can add some handling that if the link ends in Twitter.com it opens it up in safari

Image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.